Skip to main content

Repo

Struct Repo 

Source
pub struct Repo<R: ProcessRunner = JobRunner> { /* private fields */ }
Expand description

A cwd-bound, backend-agnostic VCS handle. Operations run against the bound directory (cwd); use at to get a sibling handle bound elsewhere.

Implementations§

Source§

impl Repo<JobRunner>

Source

pub fn open(dir: impl AsRef<Path>) -> Result<Self>

Detect the repository at or above dir and open a handle bound to dir, using the real job-backed runner. Errors with Error::NotARepository when no .git/.jj is found.

Source§

impl<R: ProcessRunner> Repo<R>

Source

pub fn from_git( root: impl Into<PathBuf>, cwd: impl Into<PathBuf>, client: Git<R>, ) -> Self

Build a git-backed handle from an explicit client — for a custom runner (e.g. a test seam) or a pre-configured Git.

Source

pub fn from_jj( root: impl Into<PathBuf>, cwd: impl Into<PathBuf>, client: Jj<R>, ) -> Self

Build a jj-backed handle from an explicit client.

Source

pub fn kind(&self) -> BackendKind

Which backend drives this handle.

Source

pub fn root(&self) -> &Path

The repository root detected at open time.

Source

pub fn cwd(&self) -> &Path

The directory operations run against.

Source

pub fn at(&self, dir: impl Into<PathBuf>) -> Self

A sibling handle bound to dir, sharing this handle’s client and root.

Source

pub fn git(&self) -> Option<&Git<R>>

The underlying Git client, or None when jj-backed — an escape hatch to git-only operations not on the common surface.

Source

pub fn jj(&self) -> Option<&Jj<R>>

The underlying Jj client, or None when git-backed.

Source

pub fn git_at(&self) -> Option<GitAt<'_, R>>

The git client bound to this handle’s cwd — a GitAt whose methods omit the dir argument — or None when jj-backed. The dir-free counterpart of git: repo.git_at()?.merge_continue().await?.

The returned view borrows self. To work in another worktree, bind the re-anchored handle first (the view can’t outlive a temporary at):

let wt = repo.at(wt);          // owns the re-anchored handle
let git = wt.git_at().unwrap();
git.fetch().await?;
Source

pub fn jj_at(&self) -> Option<JjAt<'_, R>>

The jj client bound to this handle’s cwd — a JjAt whose methods omit the dir argument — or None when git-backed. The dir-free counterpart of jj. For another workspace, bind the re-anchored handle first (let ws = repo.at(path); ws.jj_at()…) — see git_at.

Source

pub async fn current_branch(&self) -> Result<Option<String>>

The current branch (git) or bookmark (jj); None when detached / no bookmark on the working copy.

Source

pub async fn trunk(&self) -> Result<Option<String>>

The trunk branch/bookmark. Resolution order: the backend’s own notion (git’s origin/HEAD, jj’s trunk() revset), then a fallback to a local main, then master; None when none of those resolve.

Source

pub async fn local_branches(&self) -> Result<Vec<String>>

Local branch (git) / bookmark (jj) names.

Source

pub async fn branch_exists(&self, name: &str) -> Result<bool>

Whether a local branch/bookmark named name exists.

Source

pub async fn has_uncommitted_changes(&self) -> Result<bool>

Whether the working copy has uncommitted changes (git: a non-empty status; jj: a non-empty working-copy change @).

Source

pub async fn has_tracked_changes(&self) -> Result<bool>

Whether the working copy has uncommitted changes to tracked files.

Backend nuance: git ignores untracked files here (status --untracked-files=no); jj auto-tracks new files, so there is no untracked concept and this equals has_uncommitted_changes.

Source

pub async fn conflicted_files(&self) -> Result<Vec<String>>

Paths with unresolved merge conflicts in the working copy, repo-relative with / separators (git diff --diff-filter=U / jj resolve --list -r @). Empty when there are none.

Source

pub async fn delete_branch(&self, name: &str, force: bool) -> Result<()>

Delete a local branch (git) / bookmark (jj). force applies to git only (branch -D vs -d); jj has no force and ignores it.

Source

pub async fn rename_branch(&self, old: &str, new: &str) -> Result<()>

Rename a local branch (git) / bookmark (jj).

Source

pub async fn changed_files(&self) -> Result<Vec<FileChange>>

The working-copy changes (git status / jj diff -r @ --summary).

Source

pub async fn diff_stat(&self) -> Result<DiffStat>

Aggregate insertion/deletion counts for the working copy.

Backend nuance: git counts the working tree against HEAD (git diff, which excludes untracked files), while jj counts the @ change against its parent (which includes newly-added files). So on git a brand-new file shows in changed_files but not here, whereas on jj it shows in both. On an unborn git repo (no commits yet) the count is taken against the empty tree, so a pre-first-commit working tree stats instead of erroring.

Source

pub async fn snapshot(&self) -> Result<RepoSnapshot>

A batched RepoSnapshot of the common repo state — branch, upstream, ahead/behind, dirtiness, change count, and operation state — in one or two spawns instead of a call per field (git: status --porcelain=v2 --branch + the in-progress probe; jj: one log -r @ template + a change count). Built for prompt/status-bar/TUI refreshes. Note the asymmetry: upstream/ahead/behind are always None on jj.

Source

pub async fn commit_paths(&self, paths: &[String], message: &str) -> Result<()>

Commit exactly paths with message (git commit --only, jj commit <filesets>). Paths are repo-relative. paths must be non-empty: an empty set is refused up front, because the backends would diverge dangerously — git errors out, while jj’s commit with no filesets would silently commit the entire working copy.

Source

pub async fn fetch(&self) -> Result<()>

Fetch from the default remote (git fetch / jj git fetch).

Source

pub async fn fetch_from(&self, remote: &str) -> Result<()>

Fetch from a named remote (git fetch <remote> / jj git fetch --remote <remote>). Transient network failures are retried by the underlying client.

Source

pub async fn fetch_remote_branch(&self, branch: &str) -> Result<()>

Fetch a single branch/bookmark from origin into its remote-tracking ref (git fetch_remote_branch / jj git fetch -b). Transient network failures are retried by the underlying client.

Source

pub async fn push(&self, branch: &str) -> Result<()>

Push branch to origin (git push -u origin <branch> / jj git push -b <branch>).

The branch (jj: bookmark) must already exist locally. The two backends honestly differ in what “push” means: git pushes the ref and records the upstream (-u; idempotent on repeat pushes), while jj pushes the bookmark’s state — including deleting the remote branch if the bookmark was deleted locally. Renamed refspecs (local:remote) and non-origin remotes are git-only concepts; use the git() escape hatch (vcs_git::GitPush) for those.

Source

pub async fn checkout(&self, reference: &str) -> Result<()>

Switch the working copy to reference (git checkout / jj edit).

Source

pub async fn rebase(&self, onto: &str) -> Result<()>

Rebase the current work onto onto (git rebase / jj rebase -d). The onto is a branch/bookmark name or revision the backend understands.

Source

pub async fn try_merge(&self, source: &str) -> Result<MergeProbe>

Probe whether merging source into the current work would conflict, without leaving any trace: the probe is rolled back before returning (git: merge --no-commit --no-ff then merge --abort; jj: a merge change probed and undone via op restore).

Preconditions/behaviour:

  • git: requires a clean-enough working tree — a dirty-tree refusal propagates as a plain error, not as MergeProbe::Conflicts.
  • A failing rollback propagates as an error rather than returning a result that misdescribes the on-disk state.
  • Cancellation caveat: the rollback runs on the same client, so if the client carries a default_cancel_on token (the cancellation feature) that fires during the probe, the rollback command is cancelled too and the probe change may be left behind (Error::Cancelled surfaces). Re-probe and reset with an un-cancelled client if you need a clean tree.
Source

pub async fn abort_in_progress(&self) -> Result<OperationState>

Abort the in-progress operation, if any (git: merge --abort / rebase --abort; jj: a no-op — there are no paused operations, roll back explicitly via Jj::transaction / op_restore). Returns the fresh post-call OperationState; Clear when nothing was (or remains) in progress.

Source

pub async fn continue_in_progress(&self) -> Result<OperationState>

Continue the in-progress operation after conflict resolution (git: commit --no-edit for a merge / rebase --continue; jj: a no-op — resolving the files is the continuation). Returns the fresh post-call OperationState:

  • Conflict when unresolved paths still block continuing (also on git — unlike in_progress_state, this method does report Conflict for git), or when a continued rebase stops on the next patch’s conflict.
  • Clear when the operation finished.
Source

pub async fn in_progress_state(&self) -> Result<OperationState>

Whether the working copy is mid-operation or conflicted — see OperationState. Lets a caller decide between abort/continue without knowing the backend’s model. Note the asymmetry: this method reports Merge/Rebase (never Conflict) on git — a git conflict is that paused state, and the conflict itself surfaces on the failed op via Error::is_merge_conflict (or as Conflict from continue_in_progress) — while jj has no paused op and reports Conflict directly.

Source

pub async fn list_worktrees(&self) -> Result<Vec<WorktreeInfo>>

List attached worktrees (git) / workspaces (jj).

Source

pub async fn create_worktree( &self, path: &Path, branch: &str, base: &str, ) -> Result<CreateOutcome>

Create a worktree/workspace at path on a new branch based on base. Always CreateOutcome::Plain; a copy-on-write strategy stays in the consumer.

branch must not already exist. The jj path is two steps (workspace add then bookmark create) and is not atomic: if the bookmark step fails, the freshly-added workspace is left in place for the caller to clean up. A consumer needing resume-existing or rollback semantics should drive the underlying client via jj / git.

Source

pub async fn remove_worktree(&self, path: &Path, force: bool) -> Result<()>

Remove the worktree/workspace at path. For jj this resolves the workspace name by matching path, deletes the directory, then forgets it; a path that matches no attached jj workspace returns Error::WorktreeNotFound. (For the best-effort, never-erroring variant, see cleanup_worktree_blocking.)

Source

pub fn cleanup_worktree_blocking(&self, path: &Path) -> Result<()>

Synchronous worktree cleanup for a context that cannot .await — chiefly a Drop guard. Force-removes the worktree at path (git: worktree remove --force; jj: resolve the workspace name by path, delete the directory, then workspace forget). Best-effort and short-lived: it shells out directly (no job-containment); a jj path that matches no workspace is a no-op (Ok).

Trait Implementations§

Source§

impl<R: ProcessRunner> VcsRepo for Repo<R>

Source§

fn kind(&self) -> BackendKind

Which backend drives this handle.
Source§

fn root(&self) -> &Path

The repository root detected at open time.
Source§

fn cwd(&self) -> &Path

The directory operations run against.
Source§

fn cleanup_worktree_blocking(&self, path: &Path) -> Result<()>

Source§

fn current_branch<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Option<String>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn trunk<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Option<String>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn local_branches<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<String>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn branch_exists<'life0, 'life1, 'async_trait>( &'life0 self, name: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn has_uncommitted_changes<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn has_tracked_changes<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn conflicted_files<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<String>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn delete_branch<'life0, 'life1, 'async_trait>( &'life0 self, name: &'life1 str, force: bool, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn rename_branch<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, old: &'life1 str, new: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Source§

fn changed_files<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<FileChange>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn diff_stat<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<DiffStat>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn snapshot<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<RepoSnapshot>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn commit_paths<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, paths: &'life1 [String], message: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Source§

fn fetch<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn fetch_from<'life0, 'life1, 'async_trait>( &'life0 self, remote: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn fetch_remote_branch<'life0, 'life1, 'async_trait>( &'life0 self, branch: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn push<'life0, 'life1, 'async_trait>( &'life0 self, branch: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn checkout<'life0, 'life1, 'async_trait>( &'life0 self, reference: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn rebase<'life0, 'life1, 'async_trait>( &'life0 self, onto: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn try_merge<'life0, 'life1, 'async_trait>( &'life0 self, source: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<MergeProbe>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn abort_in_progress<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<OperationState>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn continue_in_progress<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<OperationState>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn in_progress_state<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<OperationState>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn list_worktrees<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<WorktreeInfo>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Source§

fn create_worktree<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, path: &'life1 Path, branch: &'life2 str, base: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<CreateOutcome>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Source§

fn remove_worktree<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, force: bool, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Auto Trait Implementations§

§

impl<R> Freeze for Repo<R>

§

impl<R> RefUnwindSafe for Repo<R>
where R: RefUnwindSafe,

§

impl<R> Send for Repo<R>

§

impl<R> Sync for Repo<R>

§

impl<R> Unpin for Repo<R>

§

impl<R> UnsafeUnpin for Repo<R>

§

impl<R> UnwindSafe for Repo<R>
where R: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more