diff --git a/src/cli/commands/diff.rs b/src/cli/commands/diff.rs index 80dcdd6..e661873 100644 --- a/src/cli/commands/diff.rs +++ b/src/cli/commands/diff.rs @@ -1,3 +1,14 @@ +//! `parsec diff` / `parsec conflicts` / `parsec sync` — worktree-aware diff and sync. +//! +//! ## Commands +//! - **`parsec diff [ticket]`** — show changes in a worktree against its merge-base. +//! Supports `--stat`, `--name-only`, and `--json` output modes. +//! - **`parsec conflicts`** — pre-flight check that scans all active worktrees for +//! files that diverge from a common ancestor (speculative conflict detection). +//! - **`parsec sync [ticket]`** — fast-forward an active worktree against the latest +//! upstream base branch via rebase (default) or merge. See issue #290 for the +//! full roadmap. + use std::path::Path; use anyhow::Result; @@ -8,6 +19,18 @@ use crate::git; use crate::output::{self, Mode}; use crate::worktree::WorktreeManager; +/// Show the diff between a worktree's current state and its merge-base with the +/// upstream base branch (`origin/`). +/// +/// If `ticket` is `None`, the function auto-detects the worktree by comparing +/// `cwd` against known worktree paths; returns an error if the cwd is outside +/// any parsec-managed worktree. +/// +/// Output modes: +/// - `--name-only` → list of changed file paths (human or JSON) +/// - `--stat` → diffstat summary (human or JSON) +/// - default → full unified diff piped to the terminal (human) or +/// name-status pairs (JSON) pub async fn diff( repo: &Path, ticket: Option<&str>, @@ -69,6 +92,12 @@ pub async fn diff( Ok(()) } +/// Detect files that are modified in multiple active worktrees simultaneously. +/// +/// Scans every workspace returned by [`WorktreeManager::list`] and compares +/// the set of changed files. Pairs of worktrees that touch the same path are +/// reported as potential conflicts so the developer can resolve them before +/// merging. Does **not** modify any worktree state. pub async fn conflicts(repo: &Path, mode: Mode) -> Result<()> { let config = ParsecConfig::load()?; let manager = WorktreeManager::new(repo, &config)?; @@ -80,6 +109,18 @@ pub async fn conflicts(repo: &Path, mode: Mode) -> Result<()> { Ok(()) } +/// Sync one or more worktrees with the latest state of their upstream base branch. +/// +/// Fetches `origin/` and applies either a **rebase** (default, +/// `strategy = "rebase"`) or a **merge** (`strategy = "merge"`). A failed +/// rebase/merge is automatically aborted so the worktree is left clean. +/// +/// Selection logic (in order): +/// 1. `--all` → all active worktrees +/// 2. `ticket` → the named worktree only +/// 3. auto-detect → the worktree whose path contains `cwd` +/// +/// Returns a summary of synced tickets and any failures via [`output::print_sync`]. pub async fn sync( repo: &Path, ticket: Option<&str>, diff --git a/src/cli/commands/history.rs b/src/cli/commands/history.rs index abf8b29..1ce95d7 100644 --- a/src/cli/commands/history.rs +++ b/src/cli/commands/history.rs @@ -1,3 +1,12 @@ +//! `parsec log` / `parsec log --export` / `parsec undo` — operation history and undo. +//! +//! Parsec maintains two complementary audit trails: +//! - **OpLog** (`~/.parsec/oplog.json`) — structured log of high-level parsec +//! operations (start, ship, clean, adopt, undo). Displayed by `parsec log` and +//! used by `parsec undo` to reconstruct the previous state. +//! - **ExecLog** (`.parsec/execlog.ndjson` in the repo) — low-level shell command +//! trace for debugging. Exported verbatim by `parsec log --export`. + use std::path::Path; use anyhow::{Context, Result}; @@ -7,6 +16,11 @@ use crate::errors::ErrorCode; use crate::git; use crate::output::{self, Mode}; +/// Display the parsec operation log, optionally filtered to a single ticket. +/// +/// `last` controls how many entries to show (counted from the end of the log). +/// When `ticket` is `Some`, only entries matching that ticket are shown. +/// Uses [`output::print_log`] for human/JSON rendering. pub async fn log(repo: &Path, ticket: Option<&str>, last: usize, mode: Mode) -> Result<()> { let repo_root = git::get_main_repo_root(repo).or_else(|_| git::get_repo_root(repo))?; let oplog = crate::oplog::OpLog::load(&repo_root)?; @@ -18,6 +32,9 @@ pub async fn log(repo: &Path, ticket: Option<&str>, last: usize, mode: Mode) -> Ok(()) } +/// Dump the raw execution log (ndjson) to stdout for debugging or external tooling. +/// +/// Prints a warning to stderr when the log is empty; exits cleanly in either case. pub async fn log_export(repo: &Path) -> Result<()> { let repo_root = git::get_main_repo_root(repo).or_else(|_| git::get_repo_root(repo))?; let raw = crate::execlog::read_raw(&repo_root)?; @@ -29,6 +46,19 @@ pub async fn log_export(repo: &Path) -> Result<()> { Ok(()) } +/// Undo the most recent reversible parsec operation. +/// +/// Reads the last [`OpLog`] entry and reverses it: +/// - `start` / `adopt` — removes the worktree, deletes the local branch, and +/// drops the workspace from state. +/// - `ship` / `clean` — re-creates the worktree from the local branch (or +/// restores it from `origin/` if the local ref is gone). +/// +/// `undo` itself is **not** re-undoable; attempting `parsec undo` after `parsec +/// undo` returns [`ErrorCode::E013`]. +/// +/// When `dry_run = true` the intended action is printed but no mutations are +/// performed. pub async fn undo(repo: &Path, dry_run: bool, mode: Mode) -> Result<()> { let config = ParsecConfig::load()?; let repo_root = git::get_main_repo_root(repo).or_else(|_| git::get_repo_root(repo))?;