gwm-cli 1.6.1

git worktree manager — TUI + CLI, native libgit2, per-repo bootstrap
Documentation
[package]
# crates.io package name. The bare `gwm` name is taken by an unrelated
# project (shutootaki/gwm), so we publish as `gwm-cli` — the installed
# binary and the library crate stay `gwm` (see [[bin]] / [lib] below),
# so `cargo install gwm-cli` still yields the `gwm` command.
name = "gwm-cli"
version = "1.6.1"
edition = "2021"
# MSRV is the floor required by every use in the crate, not just the newest
# one introduced here. Our own std usage sits far lower: `std::sync::LazyLock`
# (src/naming.rs) wants 1.80, `std::iter::repeat_n` (src/tui/ui.rs's countdown
# bar) wants 1.82. The floor comes from the dependency graph, and reading it
# takes a build rather than `cargo metadata`: the highest *declared*
# `rust-version` in the locked graph is 1.88 (the ratatui 0.30 stack, `time
# 0.3.47`), but `libsqlite3-sys 0.38.1`, a normal dependency pulled in by
# `rusqlite` with `bundled`, declares no `rust-version` at all and its build
# script uses `cfg_select!`, stable only since 1.95.0. Measured against the
# committed lockfile: `cargo +1.94 check --all-targets --locked` fails with
# `error[E0658]`, `cargo +1.95 check --all-targets --locked` passes.
#
# Two CI jobs hold this line. The clippy job catches an accidental *std-API*
# use above it (`clippy::incompatible_msrv` is warn-by-default, escalated to a
# hard error by `-D warnings`). The `msrv` job (#491) installs the toolchain
# declared right here and runs `cargo check --all-targets --locked`, which is
# the only check that sees a language/edition feature or a dependency that
# declares no floor of its own. See `docs/6.development/3.stability.md`
# §MSRV policy for the full story.
rust-version = "1.95"
description = "git worktree manager — TUI + CLI, native libgit2, per-repo bootstrap"
authors = ["Kylian Bardini"]
license = "MIT"
repository = "https://github.com/kbrdn1/gwm-cli"
readme = "README.md"
keywords = ["git", "worktree", "tui", "cli", "ratatui"]
categories = ["command-line-utilities", "development-tools"]

# cargo-binstall (#27): pull the prebuilt binary from the GitHub
# Release instead of compiling git2/vendored-libgit2 from source.
# Mirrors the release workflows' artefact naming — `gwm-v{version}-
# {target}.tar.gz` (`.zip` on windows) — and the in-archive layout
# `gwm-v{version}-{target}/{gwm|gwm.exe}`. `tests/binstall_metadata_tests.rs`
# pins this block against drift.
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/gwm-v{ version }-{ target }.tar.gz"
pkg-fmt = "tgz"
bin-dir = "gwm-v{ version }-{ target }/{ bin }"

[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-url = "{ repo }/releases/download/v{ version }/gwm-v{ version }-{ target }.zip"
pkg-fmt = "zip"

# `.deb` packages (issue #377) — built + attached to each stable Release by
# `release.yml` via `cargo deb --no-build --target <triple>`. The package name
# is the crate name (`gwm-cli`, not `gwm`); the installed command stays `gwm`.
# `conflicts = "gwm"` is still needed because Debian's unrelated `gwm`
# window-manager package also owns `/usr/bin/gwm`, so the two can't coexist —
# dpkg refuses cleanly instead of erroring on the file clash at unpack time.
# `depends` is set explicitly (not `$auto`) so cargo-deb skips `dpkg-shlibdeps`
# — the binary vendors both libgit2 AND zlib statically (see the libz-sys dep
# above), so glibc is its only dynamic *library* dependency. Skipping shlibdeps
# is what lets the cross-built aarch64 package be produced from an x86_64
# runner. `git` is added as a runtime dependency because gwm shells out to the
# `git` binary (sync, worktree rename, clean, TUI previews) beyond the vendored
# libgit2 — shlibdeps would never catch an exec dependency (#388).
[package.metadata.deb]
maintainer = "Kylian Bardini <onepiecekylian@gmail.com>"
copyright = "2026 Kylian Bardini"
license-file = ["LICENSE.md", "0"]
extended-description = """
git worktree manager — a terminal UI and CLI over git worktrees. Native
libgit2, per-repo .gwm.toml bootstrap (file copies, regex guards, lifecycle
hooks), and a ratatui TUI. The installed command is `gwm`."""
section = "utils"
priority = "optional"
# `>= 2.34`: the binaries are built on `ubuntu-latest`, whose CRT objects pull
# GLIBC_2.34, so this is a definite lower bound. Declaring it makes dpkg refuse
# cleanly on older glibc (RHEL 8 = 2.28, Ubuntu 20.04 = 2.31) instead of
# installing then failing at load. It is a conservative floor, not the exact
# per-arch ABI floor — computing that from the ELF in CI (and doing the same for
# the tarballs) is tracked as a follow-up (#386).
depends = "libc6 (>= 2.34), git"
conflicts = "gwm"
assets = [
  ["target/release/gwm", "usr/bin/", "755"],
  ["README.md", "usr/share/doc/gwm-cli/README.md", "644"],
  ["CHANGELOG.md", "usr/share/doc/gwm-cli/CHANGELOG.md", "644"],
]

# `.rpm` packages (issue #378) — built + attached by `release.yml` via
# `cargo generate-rpm --target <triple>`. `auto-req = "no"` disables rpm's
# dependency auto-detection (it needs the target's rpm tooling, unavailable
# when cross-packaging aarch64 from an x86_64 runner). With libgit2 and zlib
# both statically linked, glibc is the only dynamic *library* dependency; `git`
# is added as a runtime dependency because gwm shells out to the `git` binary
# (sync, worktree rename, clean, TUI previews) beyond the vendored libgit2
# (#388). Both are declared explicitly via `requires` so a minimal Fedora/RHEL
# still pulls them. (No `gwm` window-manager package exists on Fedora, so no
# rpm `conflicts` is needed.)
[package.metadata.generate-rpm]
summary = "git worktree manager — TUI + CLI, native libgit2, per-repo bootstrap"
license = "MIT"
auto-req = "no"
requires = { glibc = ">= 2.34", git = "*" }
assets = [
  { source = "target/release/gwm", dest = "/usr/bin/gwm", mode = "755" },
  { source = "README.md", dest = "/usr/share/doc/gwm-cli/README.md", mode = "644" },
  { source = "LICENSE.md", dest = "/usr/share/doc/gwm-cli/LICENSE.md", mode = "644" },
  { source = "CHANGELOG.md", dest = "/usr/share/doc/gwm-cli/CHANGELOG.md", mode = "644" },
]

[lib]
name = "gwm"
path = "src/lib.rs"

[[bin]]
name = "gwm"
path = "src/main.rs"

[features]
# `daemon` (issue #38, phase 2) gates the long-running JSON-RPC server
# over a unix domain socket. Default-on so the standard `cargo test` and
# release builds exercise it; `--no-default-features` drops the serving
# path for Windows / minimal builds (the socket impl is also
# `cfg(unix)`-gated, so it compiles to a clean runtime error there
# regardless). The cross-platform pure-RPC layer (parse / dispatch) is
# always compiled and unit-tested. No extra dependencies: the socket uses
# `std::os::unix::net`.
default = ["daemon"]
daemon = []

[dependencies]
clap = { version = "4.5", features = ["derive", "color"] }
clap_complete = "4.5"
ratatui = "0.30"
# `osc52` pulls crossterm's `clipboard` module (`CopyToClipboard`) and, with it,
# base64 — the encoder OSC52 needs. Using the framing crossterm already ships,
# and which its own tests pin, beats hand-rolling the sequence here (#367).
crossterm = { version = "0.29", features = ["osc52"] }
git2 = { version = "0.21", default-features = false, features = ["vendored-libgit2"] }
# Force a statically-linked zlib (issue #377/#378 review). git2 pulls libz-sys,
# which by default links the system `libz.so.1` dynamically — so the release
# binary, and the .deb/.rpm built from it, would need a zlib runtime package too.
# Static-linking it means everything depends only on glibc, so the packages can
# declare accurate deps without per-distro zlib package names (zlib1g / zlib /
# zlib-ng) or arch-specific sonames, and the cross-built aarch64 package is
# correct too. Applied as a Cargo feature so it propagates inside the `cross`
# container without env passthrough.
libz-sys = { version = "1", features = ["static"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "1.1"
toml_edit = "0.25"
anyhow = "1"
thiserror = "2"
dirs = "6"
regex = "1"
shellexpand = "3"
chrono = { version = "0.4", features = ["serde"] }
which = "8"
glob = "0.3"
nucleo-matcher = "0.3"
shell-words = "1"
tempfile = "3"
# Issue #35: PTY overlay for embedded lazygit / native terminal.
# portable-pty provides the cross-platform PTY pair; tui-term renders the
# vt100 parser output as a ratatui widget. tui-term 0.3 re-exports its own
# vt100 0.16 as `tui_term::vt100` — use that, not a standalone vt100 crate.
portable-pty = "0.9"
tui-term = "0.3"
# Issue #95: SHA-256 over the raw bytes of `.gwm.toml` for the TOFU
# trust ledger. Whitespace-sensitive on purpose — `rm -rf /tmp/` and
# `rm -rf /tmp /` are visually close but semantically different, so
# we hash bytes, not parsed TOML.
sha2 = "0.11"
serde_yaml_ng = "0.10.0"
rusqlite = { version = "0.40.1", features = ["bundled"] }

# Unix-only: needed for `O_NOFOLLOW` in bootstrap's TOCTOU-safe copy
# primitives (issue #93). Already pulled in transitively by git2 /
# dirs / cc, declared here for explicit use in `src/bootstrap.rs`.
[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target."cfg(windows)".dependencies]
interprocess = "2.4.2"
widestring = "1.2.1"
windows-sys = { version = "0.61.2", features = ["Win32_System_Pipes", "Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_System_Threading"] }

[target."cfg(windows)".dev-dependencies]
interprocess = "2.4.2"

[dev-dependencies]
assert_cmd = "2"
criterion = "0.8"
predicates = "3"
# Parse our own Cargo.toml in tests/binstall_metadata_tests.rs (#27) so
# the `[package.metadata.binstall]` contract is asserted structurally,
# not by string-matching. Same `toml` major as the runtime dependency.
toml = "1.1"

[[bench]]
name = "commit_graph"
harness = false

[[bench]]
name = "sidebar_recent_commits"
harness = false

[[bench]]
name = "sidebar_cache_hit"
harness = false

[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = true