GitCli.Worktree (fnord v0.9.29)
View SourceShared worktree context for listing, creating, deleting, merging, and recreating project worktrees.
Summary
Functions
Stages all changes and commits them in the given worktree directory.
Returns {:error, :nothing_to_commit} when there is nothing to commit.
Returns the default path for a conversation worktree within a project.
Copies a list of relative paths from the worktree to the source repo, creating parent directories as needed. Returns a list of results for each path attempted.
Creates a local conversation worktree under the default project conversation path.
Returns the currently checked-out branch name for the given repo root, or nil when HEAD is detached or the branch cannot be determined.
Returns the default base branch for the repository — either the remote HEAD
(e.g., main) or the current branch as a fallback.
Returns the default on-disk worktree root for a project under the user's home directory.
Removes a worktree at the given path from the repository.
Deletes a local branch from the repository. Uses -d (safe delete) which
refuses to delete unmerged branches.
Returns the diff between a base branch and a worktree branch, run from the repository root.
Returns the diff between the fork point (merge-base) of the worktree branch and its base branch. This shows all changes since the branch was created, regardless of what has happened on the base branch since.
Creates an independent copy of an existing worktree for a forked conversation. The new worktree
Enriches a raw worktree record with merge status, size, and existence information.
Returns true when the given worktree path resolves to the default
fnord-managed worktree root for the project or to a path beneath it.
Worktrees at this location are always created by fnord with an fnord-
prefixed branch, so a normalized path check is sufficient to identify
internally managed worktrees.
Removes a worktree even when it contains uncommitted changes.
Force-deletes a local branch regardless of merge status.
Returns true when the worktree at path either has uncommitted changes OR
has commits on branch that are not yet on base_branch. This is the
filesystem/git source of truth for "is there work in this worktree that
would be lost if we discarded it without merging?"
Returns true when the worktree at path has staged, unstaged, or untracked
changes that would be lost by a non-force removal.
Returns the short SHA of HEAD in the given repo root, or nil on failure.
Returns the full (unabbreviated) SHA of HEAD, suitable for use as a reset target.
Lists Git worktrees for a repository root and enriches each entry with merge status, size, and existence information.
Lists Git worktrees for a repository root without enrichment. Returns the parsed records as maps containing at least :path and optionally :branch and :base_branch. Callers that only need to filter/select should prefer this to avoid per-worktree git calls and filesystem walks.
Returns a list of one-line commit summaries in the range from..to,
newest first. Each entry is a short-sha + subject line.
Merges the checked-out worktree branch into the repository root current branch. Rebases the branch onto the target first, then fast-forward merges for a linear history. Falls back to a regular merge if the rebase fails.
Returns the merge status of a worktree branch relative to its base branch.
Normalizes stored worktree metadata into the shape expected by the context.
Normalizes the worktree sub-map within a parent metadata map, handling both atom and string keys for the worktree entry itself. Returns the parent map with a normalized :worktree value, or unchanged if no worktree is present.
Returns true when the file at path is ignored by git in the given root
directory (via .gitignore or other exclusion mechanisms). Uses
git check-ignore -q which returns exit 0 for ignored paths, 1 otherwise.
Returns the current repository root or :not_a_repo when the process is not
inside a Git repository.
Recreates a missing conversation worktree at its default path from stored metadata.
Hard-resets the repository at root to the given target ref. Used to
revert a fast-forward merge that may have advanced HEAD by multiple commits.
Types
@type recreation_result() :: %{ root: String.t(), path: String.t(), branch: String.t(), meta: worktree_meta() }
@type worktree_entry() :: %{ path: String.t(), branch: String.t() | nil, base_branch: String.t() | nil, merge_status: :ahead | :diverged | :unknown, size: non_neg_integer(), exists?: boolean() }
Functions
Stages all changes and commits them in the given worktree directory.
Returns {:error, :nothing_to_commit} when there is nothing to commit.
Returns the default path for a conversation worktree within a project.
@spec copy_ignored_files(String.t(), String.t(), [String.t()]) :: [ {:ok, String.t()} | {:error, String.t(), term()} ]
Copies a list of relative paths from the worktree to the source repo, creating parent directories as needed. Returns a list of results for each path attempted.
@spec create(String.t(), String.t(), String.t() | nil) :: {:ok, worktree_entry()} | {:error, atom()}
Creates a local conversation worktree under the default project conversation path.
Returns the currently checked-out branch name for the given repo root, or nil when HEAD is detached or the branch cannot be determined.
Returns the default base branch for the repository — either the remote HEAD
(e.g., main) or the current branch as a fallback.
Returns the default on-disk worktree root for a project under the user's home directory.
Removes a worktree at the given path from the repository.
Deletes a local branch from the repository. Uses -d (safe delete) which
refuses to delete unmerged branches.
Returns the diff between a base branch and a worktree branch, run from the repository root.
@spec diff_from_fork_point(String.t(), String.t(), String.t()) :: {:ok, String.t()} | {:error, atom()}
Returns the diff between the fork point (merge-base) of the worktree branch and its base branch. This shows all changes since the branch was created, regardless of what has happened on the base branch since.
@spec duplicate(String.t(), worktree_meta(), String.t()) :: {:ok, worktree_entry()} | {:error, atom()}
Creates an independent copy of an existing worktree for a forked conversation. The new worktree:
- lives at the default conversation path for
new_conversation_id - is on a fresh branch named
fnord-<new_conversation_id>that points at the source branch's HEAD (so all source commits are inherited along with the same merge-base relative to the original base branch) - inherits the source's
base_branchso future merges still target the original base (typicallymain), not the source's fnord-* branch - carries the source's uncommitted state (modified, deleted, and untracked files) replicated via direct file ops, leaving the source worktree untouched
On any failure the new worktree and branch are torn down before returning so the caller can fall back cleanly.
Enriches a raw worktree record with merge status, size, and existence information.
Returns true when the given worktree path resolves to the default
fnord-managed worktree root for the project or to a path beneath it.
Worktrees at this location are always created by fnord with an fnord-
prefixed branch, so a normalized path check is sufficient to identify
internally managed worktrees.
Removes a worktree even when it contains uncommitted changes.
Force-deletes a local branch regardless of merge status.
@spec has_changes_to_merge?( String.t(), String.t(), String.t() | nil, String.t() | nil ) :: boolean()
Returns true when the worktree at path either has uncommitted changes OR
has commits on branch that are not yet on base_branch. This is the
filesystem/git source of truth for "is there work in this worktree that
would be lost if we discarded it without merging?"
Used by Cmd.Ask to gate the end-of-session merge flow without relying on agent-side tool tracking, which can miss edits made via cmd_tool, frobs, MCP servers, or any code path the heuristic does not enumerate.
Returns true when the worktree at path has staged, unstaged, or untracked
changes that would be lost by a non-force removal.
Returns the short SHA of HEAD in the given repo root, or nil on failure.
Returns the full (unabbreviated) SHA of HEAD, suitable for use as a reset target.
@spec list(String.t() | nil) :: {:ok, [worktree_entry()]} | {:error, atom()}
Lists Git worktrees for a repository root and enriches each entry with merge status, size, and existence information.
Lists Git worktrees for a repository root without enrichment. Returns the parsed records as maps containing at least :path and optionally :branch and :base_branch. Callers that only need to filter/select should prefer this to avoid per-worktree git calls and filesystem walks.
Returns a list of one-line commit summaries in the range from..to,
newest first. Each entry is a short-sha + subject line.
Merges the checked-out worktree branch into the repository root current branch. Rebases the branch onto the target first, then fast-forward merges for a linear history. Falls back to a regular merge if the rebase fails.
@spec merge_status(String.t(), String.t(), String.t() | nil, String.t() | nil) :: :ahead | :diverged | :unknown
Returns the merge status of a worktree branch relative to its base branch.
Returns :ahead when the worktree branch contains the base branch and
:unknown when the worktree path or any branch metadata is unavailable.
Any other git result is reported as :diverged so callers can treat the
branch as needing manual attention before merge.
@spec normalize_worktree_meta(map()) :: worktree_meta()
Normalizes stored worktree metadata into the shape expected by the context.
Normalizes the worktree sub-map within a parent metadata map, handling both atom and string keys for the worktree entry itself. Returns the parent map with a normalized :worktree value, or unchanged if no worktree is present.
Returns true when the file at path is ignored by git in the given root
directory (via .gitignore or other exclusion mechanisms). Uses
git check-ignore -q which returns exit 0 for ignored paths, 1 otherwise.
Returns the current repository root or :not_a_repo when the process is not
inside a Git repository.
@spec recreate_conversation_worktree(String.t(), String.t(), worktree_meta()) :: {:ok, worktree_meta()} | {:error, atom()}
Recreates a missing conversation worktree at its default path from stored metadata.
@spec recursive_size(String.t()) :: non_neg_integer()
Hard-resets the repository at root to the given target ref. Used to
revert a fast-forward merge that may have advanced HEAD by multiple commits.