pub struct Repo<R: ProcessRunner = JobRunner> { /* private fields */ }Expand description
Implementations§
Source§impl<R: ProcessRunner> Repo<R>
impl<R: ProcessRunner> Repo<R>
Sourcepub fn from_git(
root: impl Into<PathBuf>,
cwd: impl Into<PathBuf>,
client: Git<R>,
) -> Self
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.
Sourcepub fn from_jj(
root: impl Into<PathBuf>,
cwd: impl Into<PathBuf>,
client: Jj<R>,
) -> Self
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.
Sourcepub fn kind(&self) -> BackendKind
pub fn kind(&self) -> BackendKind
Which backend drives this handle.
Sourcepub fn at(&self, dir: impl Into<PathBuf>) -> Self
pub fn at(&self, dir: impl Into<PathBuf>) -> Self
A sibling handle bound to dir, sharing this handle’s client and root.
Sourcepub fn git(&self) -> Option<&Git<R>>
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.
Sourcepub fn git_at(&self) -> Option<GitAt<'_, R>>
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?;Sourcepub async fn current_branch(&self) -> Result<Option<String>>
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.
Sourcepub async fn trunk(&self) -> Result<Option<String>>
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.
Sourcepub async fn local_branches(&self) -> Result<Vec<String>>
pub async fn local_branches(&self) -> Result<Vec<String>>
Local branch (git) / bookmark (jj) names.
Sourcepub async fn branch_exists(&self, name: &str) -> Result<bool>
pub async fn branch_exists(&self, name: &str) -> Result<bool>
Whether a local branch/bookmark named name exists.
Sourcepub async fn has_uncommitted_changes(&self) -> Result<bool>
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 @).
Sourcepub async fn has_tracked_changes(&self) -> Result<bool>
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.
Sourcepub async fn conflicted_files(&self) -> Result<Vec<String>>
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.
Sourcepub async fn delete_branch(&self, name: &str, force: bool) -> Result<()>
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.
Sourcepub async fn rename_branch(&self, old: &str, new: &str) -> Result<()>
pub async fn rename_branch(&self, old: &str, new: &str) -> Result<()>
Rename a local branch (git) / bookmark (jj).
Sourcepub async fn changed_files(&self) -> Result<Vec<FileChange>>
pub async fn changed_files(&self) -> Result<Vec<FileChange>>
The working-copy changes (git status / jj diff -r @ --summary).
Sourcepub async fn diff_stat(&self) -> Result<DiffStat>
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.
Sourcepub async fn snapshot(&self) -> Result<RepoSnapshot>
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.
Sourcepub async fn commit_paths(&self, paths: &[String], message: &str) -> Result<()>
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.
Sourcepub async fn fetch(&self) -> Result<()>
pub async fn fetch(&self) -> Result<()>
Fetch from the default remote (git fetch / jj git fetch).
Sourcepub async fn fetch_from(&self, remote: &str) -> Result<()>
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.
Sourcepub async fn fetch_remote_branch(&self, branch: &str) -> Result<()>
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.
Sourcepub async fn push(&self, branch: &str) -> Result<()>
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.
Sourcepub async fn checkout(&self, reference: &str) -> Result<()>
pub async fn checkout(&self, reference: &str) -> Result<()>
Switch the working copy to reference (git checkout / jj edit).
Sourcepub async fn rebase(&self, onto: &str) -> Result<()>
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.
Sourcepub async fn try_merge(&self, source: &str) -> Result<MergeProbe>
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_ontoken (thecancellationfeature) that fires during the probe, the rollback command is cancelled too and the probe change may be left behind (Error::Cancelledsurfaces). Re-probe and reset with an un-cancelled client if you need a clean tree.
Sourcepub async fn abort_in_progress(&self) -> Result<OperationState>
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.
Sourcepub async fn continue_in_progress(&self) -> Result<OperationState>
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:
Conflictwhen unresolved paths still block continuing (also on git — unlikein_progress_state, this method does reportConflictfor git), or when a continued rebase stops on the next patch’s conflict.Clearwhen the operation finished.
Sourcepub async fn in_progress_state(&self) -> Result<OperationState>
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.
Sourcepub async fn list_worktrees(&self) -> Result<Vec<WorktreeInfo>>
pub async fn list_worktrees(&self) -> Result<Vec<WorktreeInfo>>
List attached worktrees (git) / workspaces (jj).
Sourcepub async fn create_worktree(
&self,
path: &Path,
branch: &str,
base: &str,
) -> Result<CreateOutcome>
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.
Sourcepub async fn remove_worktree(&self, path: &Path, force: bool) -> Result<()>
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.)
Sourcepub fn cleanup_worktree_blocking(&self, path: &Path) -> Result<()>
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>
impl<R: ProcessRunner> VcsRepo for Repo<R>
Source§fn kind(&self) -> BackendKind
fn kind(&self) -> BackendKind
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,
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,
Repo::current_branch.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,
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,
Repo::trunk.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,
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,
Repo::local_branches.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,
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,
Repo::branch_exists.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,
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,
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,
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,
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,
Repo::delete_branch.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,
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,
Repo::rename_branch.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,
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,
Repo::changed_files.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,
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,
Repo::diff_stat.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,
fn snapshot<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<RepoSnapshot>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Repo::snapshot.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,
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,
Repo::commit_paths.Source§fn fetch<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn fetch<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Repo::fetch.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,
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,
Repo::fetch_from.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,
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,
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,
Repo::push.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,
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,
Repo::checkout.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,
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,
Repo::rebase.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,
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,
Repo::try_merge.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,
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,
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,
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,
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,
Repo::list_worktrees.