vcs

package
v0.15.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package vcs abstracts git and jj repository operations behind a common Operations interface.

Package vcs reads and mutates a checkout through git or jj behind one interface.

It holds data and predicates only. Anything that renders a glyph, a placeholder, or a human-readable duration belongs to the consumer's display layer, because two tools reading the same checkout render it differently.

Index

Constants

This section is empty.

Variables

View Source
var (
	BranchCache = cache.NewRegistered[[]BranchInfo](defaultTTL)
	CommitCache = cache.NewRegistered[[]CommitInfo](defaultTTL)
)

BranchCache and CommitCache hold what a checkout's refs and log said, keyed by the checkout rather than by the object store it borrows. A worktree and its parent read the same refs, but both values are relative to whichever HEAD asked: the branch list carries the current-branch marker and the log starts at HEAD.

View Source
var DefaultBranchNames = []string{"main", "master", "trunk"}

DefaultBranchNames are the conventional primary branch names, assumed wherever a repo's real default branch has not been resolved from its remote.

View Source
var ErrCommandFailed = errors.New("command failed")

ErrCommandFailed wraps a non-zero exit from the underlying git/jj CLI.

View Source
var ErrDiverged = errors.New("local branch has diverged from its upstream")

ErrDiverged reports that the working branch and its upstream share no fast-forward path in either direction, which is what a rebase or force-push of the upstream leaves behind. Neither side is a superset of the other, so no automatic pull is safe: only a caller that knows the local commits are disposable (its own prior checkout of the same branch, not independent work) can decide what happens next.

View Source
var ErrUnexpectedOutput = errors.New("unexpected command output")

ErrUnexpectedOutput wraps a CLI output that didn't match the expected format.

Functions

func BranchCacheKey

func BranchCacheKey(repoPath string) string

BranchCacheKey builds the branch list cache key for a checkout.

func CheckoutIdentity

func CheckoutIdentity(repoPath string) string

CheckoutIdentity returns the path of the repository whose refs repoPath works on. A git worktree and a jj workspace share their parent's object store and branch list, so both resolve to the parent; every other checkout resolves to itself. Fleet-wide joins key on this to avoid counting one branch once per checkout that can see it. The answer comes from the pointer files git and jj write into a linked checkout, so it costs no subprocess and works even for a repo that no longer exists.

func CommitCacheKey

func CommitCacheKey(repoPath string, count int) string

CommitCacheKey builds the commit log cache key for a checkout, keyed by depth because a deeper log is a different value.

func DefaultBranchName added in v0.7.0

func DefaultBranchName(ctx context.Context, repoPath string) (string, bool)

DefaultBranchName names the repository's default branch, reporting whether one was found. Unlike DefaultBranchHead it answers for a repository whose remote advertises no HEAD, by probing for a conventional name.

func DetachedBranchLabel

func DetachedBranchLabel(shortHash string) string

DetachedBranchLabel formats a detached HEAD's short commit as the branch label shown wherever a branch name would go.

func ExtractRepoPath

func ExtractRepoPath(remoteURL string) string

ExtractRepoPath derives an "owner/repo" style path from a git remote URL.

func GetGitHubEnv

func GetGitHubEnv(repoPath string) []string

GetGitHubEnv returns extra environment variables needed for the gh CLI to work in a jj-colocated repo.

func HeadSHA

func HeadSHA(ctx context.Context, repoPath string) (string, error)

HeadSHA returns the commit the working copy is checked out on, which is the commit a code host compares against.

A colocated jj repository is read through its own .git rather than through a revset, because jj removed git_head() and a version that has dropped it answers "Function `git_head` doesn't exist", failing every read of the checkout. Git answers the same question with nothing left to rename, and it does not snapshot the working copy the way any jj command does. A jj repository with no .git has no commit a code host knows about, so the working copy's first parent stands in, which is what jj names as git_head()'s replacement.

func IsDefaultBranchName

func IsDefaultBranchName(name string) bool

IsDefaultBranchName reports whether name is one of the conventional default-branch names that cleanup should never treat as a feature branch.

func IsDetachedBranch

func IsDetachedBranch(label string) bool

IsDetachedBranch reports whether label came from DetachedBranchLabel, so views can say "detached" rather than treating the commit as a branch name.

func IsRepo

func IsRepo(path string) bool

IsRepo reports whether path is a git or jj repository root.

func PullFastForward

func PullFastForward(ctx context.Context, repoPath string) error

PullFastForward advances the current branch to its upstream and changes nothing when it cannot outright fast-forward, reporting ErrDiverged rather than attempting a merge when the two histories have split.

Never add --autostash. On git 2.x an --ff-only --autostash pull against a dirty file the pull also touches exits 0 while leaving UU conflict markers in the tree and the stash still on the stack, so the exit code says the working tree is fine when it is conflicted.

func RemoteBranches added in v0.7.0

func RemoteBranches(ctx context.Context, repoPath string) ([]string, error)

RemoteBranches names the branches the remote holds, sorted and deduplicated, with the "origin/" prefix and origin/HEAD dropped.

func RemoteIdentity

func RemoteIdentity(remoteURL string) string

RemoteIdentity derives a key for the repository a remote URL names: "host/owner/repo", lowercased. Every checkout of one remote shares it, whatever URL form or casing each was cloned with, since GitHub treats Owner/Repo and owner/repo as one repository. The host is part of the key so an Enterprise "acme/tools" does not collide with a github.com one. A URL that names no repository yields "".

Unlike ExtractRepoPath this keeps the full path, so a GitLab subgroup is distinct from a repo of the same name under another subgroup.

func RemoteIdentityFor

func RemoteIdentityFor(ctx context.Context, repoPath string) string

RemoteIdentityFor resolves the RemoteIdentity of the checkout at repoPath, costing one git/jj call. Callers holding a RepoSummary should read its RemoteID instead of paying for this again.

func ResetHardToUpstream added in v0.15.1

func ResetHardToUpstream(ctx context.Context, repoPath string) error

ResetHardToUpstream discards the working branch's commits and points it at its upstream. The caller must have already established that discarding those commits loses nothing real (they came from second-look's own prior checkout, not from work done on top of it): this is not reversible in the working tree.

func SetExternalDiffCommand

func SetExternalDiffCommand(command string)

SetExternalDiffCommand replaces the external diff command, in the form git's diff.external takes. Intended for startup config application only; not safe to call concurrently with a diff read.

func Stamp

func Stamp(repoPath string) cache.Stamp

Stamp fingerprints what the checkout at repoPath looks like right now, without spawning a subprocess: the OID HEAD resolves to, the current branch and the upstream it tracks, that upstream's remote-tracking OID, and the mtimes of the ref files a commit, switch, push, or fetch touches. Cheap enough to take on every render. It stats refs/heads and refs/remotes/<remote> directly, since a directory's mtime tracks only its own entries.

A jj repo is stamped on its git refs alone, which a colocated repo keeps in step with every operation. A workspace with no .git yields cache.NoStamp, which proves nothing and so leaves values derived from it uncached rather than served stale.

Types

type BlameLine added in v0.14.0

type BlameLine struct {
	When   time.Time
	Commit string
	Author string
	Email  string
	Line   int
}

BlameLine attributes one line of a file to the change that introduced it.

type BranchInfo

type BranchInfo struct {
	LastCommit time.Time
	Name       string
	Upstream   string
	Head       string
	Ahead      int
	Behind     int
	IsCurrent  bool
	IsRemote   bool
}

BranchInfo summarizes a single branch's tracking state.

type CommitInfo

type CommitInfo struct {
	Date      time.Time
	Hash      string
	ShortHash string
	Subject   string
	Author    string
}

CommitInfo summarizes a single commit.

type DefaultBranch

type DefaultBranch struct {
	Name string
	SHA  string
}

DefaultBranch is a repo's default branch and the commit it points at, read from origin/HEAD.

func DefaultBranchHead

func DefaultBranchHead(ctx context.Context, repoPath string) (DefaultBranch, error)

DefaultBranchHead resolves origin/HEAD. It fails for a repo whose remote has no HEAD ref, which is the case for one that has never been fetched.

type DefaultBranchResolver added in v0.7.0

type DefaultBranchResolver interface {
	ResolveDefaultBranch(ctx context.Context, repoPath string) (string, bool)
}

DefaultBranchResolver names a repository's default branch.

type DetailReader

type DetailReader interface {
	GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)
	GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)
	GetStashList(ctx context.Context, repoPath string) ([]StashDetail, error)
	GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)
	// GetNewestModifiedFile names the working tree's most recently modified
	// uncommitted file and when it was touched. Name is empty with a nil
	// error for a clean tree, where there is nothing uncommitted to report.
	GetNewestModifiedFile(ctx context.Context, repoPath string) (name string, modTime time.Time, err error)
	// Blame attributes each line of path in ranges to the change that introduced
	// it, in ascending line order. Empty ranges annotate the whole file.
	//
	// Cost differs by backend: git scopes the walk with -L, so it is proportional
	// to the ranges asked for, while jj has no line-range flag and annotates the
	// whole file, filtering here.
	Blame(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)
}

DetailReader answers drill-down queries about a repository's branches, stashes, worktrees, and commit history.

type GitConfigOverride

type GitConfigOverride struct {
	Key         string
	LocalValue  string
	GlobalValue string
}

GitConfigOverride is a local git config value that differs from the same key's global value.

type GitOperations

type GitOperations struct{}

GitOperations implements Operations for git repositories.

func NewGitOperations

func NewGitOperations() *GitOperations

NewGitOperations returns a GitOperations.

func (*GitOperations) ApplyStash

func (g *GitOperations) ApplyStash(ctx context.Context, repoPath string, index int) (bool, string, error)

ApplyStash implements Operations.

func (*GitOperations) Blame added in v0.14.0

func (g *GitOperations) Blame(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)

Blame implements Operations. One git process handles every range: -L may repeat and overlapping ranges are allowed, so the walk stays proportional to what was asked for rather than the whole file.

func (*GitOperations) CleanupMergedBranches

func (g *GitOperations) CleanupMergedBranches(
	ctx context.Context, repoPath string, squashMerged []string,
) (bool, string, error)

CleanupMergedBranches implements Operations. The squashMerged parameter names branches already verified by the caller (via merged PR head OIDs) as squash-merged: `git branch --merged` misses these because the squash commit differs from the original branch tip, so they're deleted with `-D` instead of `-d`, and only when not the current branch and not checked out in a worktree.

func (*GitOperations) CompareBranches

func (g *GitOperations) CompareBranches(ctx context.Context, repoPath, branch, target string) (int, int, error)

CompareBranches implements Operations. Git's rev-list comparison works for any two local refs, so this reuses GetAheadBehind with the default branch as the right-hand side.

func (*GitOperations) DeleteBranch

func (g *GitOperations) DeleteBranch(ctx context.Context, repoPath, branch string, force bool) (bool, string, error)

DeleteBranch implements Operations.

func (*GitOperations) DropStash

func (g *GitOperations) DropStash(ctx context.Context, repoPath string, index int) (bool, string, error)

DropStash implements Operations.

func (*GitOperations) ExternalDiffCommand

func (g *GitOperations) ExternalDiffCommand(ctx context.Context, repoPath string) string

ExternalDiffCommand is the viewer a patch from repoPath renders through: the configured override, or whatever git itself would run for diff.external. It is empty when neither names one, and the caller then reads a plain patch.

func (*GitOperations) FetchAll

func (g *GitOperations) FetchAll(ctx context.Context, repoPath string) (bool, string, error)

FetchAll implements Operations.

func (*GitOperations) GetAheadBehind

func (g *GitOperations) GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (int, int, error)

GetAheadBehind implements Operations.

func (*GitOperations) GetBranchList

func (g *GitOperations) GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)

GetBranchList implements Operations.

func (*GitOperations) GetCommitLog

func (g *GitOperations) GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)

GetCommitLog implements Operations.

func (*GitOperations) GetConflictedCount

func (g *GitOperations) GetConflictedCount(ctx context.Context, repoPath string) (int, error)

GetConflictedCount reports the number of files with merge conflicts.

func (*GitOperations) GetCurrentBranch

func (g *GitOperations) GetCurrentBranch(ctx context.Context, repoPath string) (string, error)

GetCurrentBranch implements Operations.

func (*GitOperations) GetLastModified

func (g *GitOperations) GetLastModified(ctx context.Context, repoPath string) (int64, error)

GetLastModified implements Operations.

func (*GitOperations) GetNewestModifiedFile

func (g *GitOperations) GetNewestModifiedFile(ctx context.Context, repoPath string) (string, time.Time, error)

GetNewestModifiedFile implements Operations.

func (*GitOperations) GetRemoteURL

func (g *GitOperations) GetRemoteURL(ctx context.Context, repoPath string) (string, error)

GetRemoteURL implements Operations.

func (*GitOperations) GetRepoSummary

func (g *GitOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)

GetRepoSummary implements Operations.

func (*GitOperations) GetStagedCount

func (g *GitOperations) GetStagedCount(ctx context.Context, repoPath string) (int, error)

GetStagedCount reports the number of staged files.

func (*GitOperations) GetStashList

func (g *GitOperations) GetStashList(ctx context.Context, repoPath string) ([]StashDetail, error)

GetStashList implements Operations.

func (*GitOperations) GetUnstagedCount

func (g *GitOperations) GetUnstagedCount(ctx context.Context, repoPath string) (int, error)

GetUnstagedCount reports the number of unstaged, modified files.

func (*GitOperations) GetUntrackedCount

func (g *GitOperations) GetUntrackedCount(ctx context.Context, repoPath string) (int, error)

GetUntrackedCount reports the number of untracked files.

func (*GitOperations) GetUpstream

func (g *GitOperations) GetUpstream(ctx context.Context, repoPath, branch string) (string, error)

GetUpstream implements Operations.

func (*GitOperations) GetWorktreeList

func (g *GitOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)

GetWorktreeList implements Operations.

func (*GitOperations) PreviewMergedBranches

func (g *GitOperations) PreviewMergedBranches(ctx context.Context, repoPath string) (string, []string, error)

PreviewMergedBranches reports the default branch and the local branches fully merged into it, without deleting anything. Used by the `:cleanup --dry-run` preview; not part of the Mutator interface since it's read-only.

func (*GitOperations) PruneRemote

func (g *GitOperations) PruneRemote(ctx context.Context, repoPath string) (bool, string, error)

PruneRemote implements Operations.

func (*GitOperations) PushBranch

func (g *GitOperations) PushBranch(
	ctx context.Context, repoPath, branch string, setUpstream bool,
) (bool, string, error)

PushBranch implements Operations.

func (*GitOperations) RemoteBranches added in v0.7.0

func (g *GitOperations) RemoteBranches(ctx context.Context, repoPath string) ([]string, error)

RemoteBranches implements RemoteBranchLister by reading the remote-tracking refs, which name every branch the remote had at the last fetch.

func (*GitOperations) ResolveDefaultBranch added in v0.7.0

func (g *GitOperations) ResolveDefaultBranch(ctx context.Context, repoPath string) (string, bool)

ResolveDefaultBranch returns the repository's default branch and whether one was found. It prefers the remote's advertised HEAD (`git symbolic-ref refs/remotes/origin/HEAD`), which reflects the actual default even when it's neither "main" nor "master", falling back to probing for local main/master when no such ref exists (e.g. no remote, or origin/HEAD was never set).

func (*GitOperations) StashDiff

func (g *GitOperations) StashDiff(ctx context.Context, repoPath string, index int) (string, error)

StashDiff is one stash's full patch, for the focused view's detail pane once the operator asks past the diffstat. Read-only and git-only, so it sits outside the Operations interface alongside StashDiffstat.

func (*GitOperations) StashDiffExternal

func (*GitOperations) StashDiffExternal(
	ctx context.Context, repoPath string, index, width int, command string,
) (string, error)

StashDiffExternal is one stash's patch rendered by an external diff command rather than by git. A viewer running without a terminal assumes eighty columns and drops its color, so the environment says otherwise. A flag the command itself carries still wins over any of it.

func (*GitOperations) StashDiffstat

func (g *GitOperations) StashDiffstat(ctx context.Context, repoPath string, index int) (string, error)

StashDiffstat summarizes what one stash changes, for the focused view's detail pane. Read-only and git-only, so it sits outside the Operations interface alongside PreviewMergedBranches.

func (*GitOperations) SwitchBranch

func (g *GitOperations) SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)

SwitchBranch implements Operations.

func (*GitOperations) UncommittedDiff

func (g *GitOperations) UncommittedDiff(ctx context.Context, repoPath string) (string, error)

UncommittedDiff is the working tree's full patch against HEAD (staged and unstaged changes together), for the focused view's detail pane. Untracked files carry no diff, so they never appear here even though the Status panel's file counts include them.

func (*GitOperations) UncommittedDiffExternal

func (*GitOperations) UncommittedDiffExternal(
	ctx context.Context, repoPath string, width int, command string,
) (string, error)

UncommittedDiffExternal is the working tree's patch against HEAD rendered by an external diff command rather than by git, for the same reasons StashDiffExternal sets its environment.

func (*GitOperations) UncommittedDiffstat

func (g *GitOperations) UncommittedDiffstat(ctx context.Context, repoPath string) (string, error)

UncommittedDiffstat summarizes the working tree's changes against HEAD, for the focused view's detail pane once the operator asks past the full diff.

func (*GitOperations) VCSType

func (*GitOperations) VCSType() Type

VCSType implements Operations.

type JJOperations

type JJOperations struct{}

JJOperations implements Operations for jj (Jujutsu) repositories.

func NewJJOperations

func NewJJOperations() *JJOperations

NewJJOperations returns a JJOperations.

func (*JJOperations) ApplyStash

func (*JJOperations) ApplyStash(_ context.Context, _ string, _ int) (bool, string, error)

ApplyStash implements Operations. JJ has no stash: a change is already a change, so there is nothing to restore.

func (*JJOperations) Blame added in v0.14.0

func (j *JJOperations) Blame(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)

Blame implements Operations. There is no jj flag for a line range, so this annotates the whole file and filters to ranges here; an empty ranges keeps everything.

func (*JJOperations) CleanupMergedBranches

func (j *JJOperations) CleanupMergedBranches(
	ctx context.Context, repoPath string, squashMerged []string,
) (bool, string, error)

CleanupMergedBranches implements Operations. The squashMerged parameter names bookmarks already verified by the caller (via merged PR head OIDs) as squash-merged. `jj bookmark delete` doesn't distinguish a true merge from a squash merge, so squash-merged bookmarks are deleted the same way as fully-merged ones.

func (*JJOperations) CompareBranches

func (j *JJOperations) CompareBranches(ctx context.Context, repoPath, branch, target string) (int, int, error)

CompareBranches implements Operations. There is no rev-list equivalent in jj, so ahead/behind are the sizes of the `::branch ~ ::target` and `::target ~ ::branch` revsets. Bookmark names are quoted so names with slashes resolve.

func (*JJOperations) DeleteBranch

func (j *JJOperations) DeleteBranch(ctx context.Context, repoPath, branch string, _ bool) (bool, string, error)

DeleteBranch implements Operations. Bookmarks carry no merged/unmerged distinction for the delete itself, so force changes nothing here.

func (*JJOperations) DropStash

func (*JJOperations) DropStash(_ context.Context, _ string, _ int) (bool, string, error)

DropStash implements Operations. See ApplyStash.

func (*JJOperations) FetchAll

func (j *JJOperations) FetchAll(ctx context.Context, repoPath string) (bool, string, error)

FetchAll implements Operations.

func (*JJOperations) GetAheadBehind

func (j *JJOperations) GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (int, int, error)

GetAheadBehind implements Operations.

func (*JJOperations) GetBranchList

func (j *JJOperations) GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)

GetBranchList implements Operations.

func (*JJOperations) GetCommitLog

func (j *JJOperations) GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)

GetCommitLog implements Operations.

func (*JJOperations) GetConflictedCount

func (*JJOperations) GetConflictedCount(_ context.Context, _ string) (int, error)

GetConflictedCount always returns 0: conflict detection isn't implemented for jj.

func (*JJOperations) GetCurrentBranch

func (j *JJOperations) GetCurrentBranch(ctx context.Context, repoPath string) (string, error)

GetCurrentBranch implements Operations.

func (*JJOperations) GetLastModified

func (j *JJOperations) GetLastModified(ctx context.Context, repoPath string) (int64, error)

GetLastModified implements Operations.

func (*JJOperations) GetNewestModifiedFile

func (*JJOperations) GetNewestModifiedFile(_ context.Context, _ string) (string, time.Time, error)

GetNewestModifiedFile implements Operations. A jj working copy is always the tip of a change, so there is no separate porcelain read to comb for a most-recently-touched file the way git's status list gives one.

func (*JJOperations) GetRemoteURL

func (j *JJOperations) GetRemoteURL(ctx context.Context, repoPath string) (string, error)

GetRemoteURL implements Operations.

func (*JJOperations) GetRepoSummary

func (j *JJOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)

GetRepoSummary implements Operations.

func (*JJOperations) GetStagedCount

func (*JJOperations) GetStagedCount(_ context.Context, _ string) (int, error)

GetStagedCount always returns 0: jj has no separate staging area.

func (*JJOperations) GetStashList

func (*JJOperations) GetStashList(_ context.Context, _ string) ([]StashDetail, error)

GetStashList implements Operations.

func (*JJOperations) GetUnstagedCount

func (j *JJOperations) GetUnstagedCount(ctx context.Context, repoPath string) (int, error)

GetUnstagedCount reports the number of modified files in the working copy.

func (*JJOperations) GetUntrackedCount

func (*JJOperations) GetUntrackedCount(_ context.Context, _ string) (int, error)

GetUntrackedCount always returns 0: jj automatically tracks all files.

func (*JJOperations) GetUpstream

func (j *JJOperations) GetUpstream(ctx context.Context, repoPath, branch string) (string, error)

GetUpstream implements Operations.

func (*JJOperations) GetWorktreeList

func (j *JJOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)

GetWorktreeList implements Operations.

func (*JJOperations) PreviewMergedBranches

func (j *JJOperations) PreviewMergedBranches(ctx context.Context, repoPath string) (string, []string, error)

PreviewMergedBranches reports the default bookmark and the bookmarks fully merged into it, without deleting anything. Used by the `:cleanup --dry-run` preview; not part of the Mutator interface since it's read-only.

func (*JJOperations) PruneRemote

func (*JJOperations) PruneRemote(_ context.Context, _ string) (bool, string, error)

PruneRemote implements Operations.

func (*JJOperations) PushBranch

func (j *JJOperations) PushBranch(
	ctx context.Context, repoPath, branch string, setUpstream bool,
) (bool, string, error)

PushBranch implements Operations. Pushing a bookmark carries the tags on the commits it points at, so --follow-tags has no jj equivalent.

func (*JJOperations) RemoteBranches added in v0.7.0

func (j *JJOperations) RemoteBranches(ctx context.Context, repoPath string) ([]string, error)

RemoteBranches implements RemoteBranchLister. A bookmark listing already covers every remote, so the remote names come out of the same read the branch list uses.

func (*JJOperations) ResolveDefaultBranch added in v0.7.0

func (j *JJOperations) ResolveDefaultBranch(ctx context.Context, repoPath string) (string, bool)

ResolveDefaultBranch implements DefaultBranchResolver for jj by looking for a conventional name among the bookmarks, since a jj repository advertises no HEAD of its own.

func (*JJOperations) SwitchBranch

func (j *JJOperations) SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)

SwitchBranch implements Operations, moving the working copy onto the bookmark's own change rather than creating a child change.

func (*JJOperations) VCSType

func (*JJOperations) VCSType() Type

VCSType implements Operations.

type LineRange added in v0.14.0

type LineRange struct{ From, To int }

LineRange is an inclusive, 1-based span of a file.

type MockOperations

type MockOperations struct {
	GetRepoSummaryFn        func(ctx context.Context, repoPath string) (RepoSummary, error)
	GetCurrentBranchFn      func(ctx context.Context, repoPath string) (string, error)
	GetUpstreamFn           func(ctx context.Context, repoPath, branch string) (string, error)
	GetAheadBehindFn        func(ctx context.Context, repoPath, branch, upstream string) (int, int, error)
	CompareBranchesFn       func(ctx context.Context, repoPath, branch, target string) (int, int, error)
	GetBranchListFn         func(ctx context.Context, repoPath string) ([]BranchInfo, error)
	GetStashListFn          func(ctx context.Context, repoPath string) ([]StashDetail, error)
	GetNewestModifiedFileFn func(ctx context.Context, repoPath string) (string, time.Time, error)
	GetWorktreeListFn       func(ctx context.Context, repoPath string) ([]WorktreeInfo, error)
	GetCommitLogFn          func(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)
	GetLastModifiedFn       func(ctx context.Context, repoPath string) (int64, error)
	GetRemoteURLFn          func(ctx context.Context, repoPath string) (string, error)
	VCSTypeFn               func() Type
	FetchAllFn              func(ctx context.Context, repoPath string) (bool, string, error)
	PruneRemoteFn           func(ctx context.Context, repoPath string) (bool, string, error)
	PushBranchFn            func(ctx context.Context, repoPath, branch string, setUpstream bool) (bool, string, error)
	SwitchBranchFn          func(ctx context.Context, repoPath, branch string) (bool, string, error)
	CleanupMergedBranchesFn func(ctx context.Context, repoPath string, squashMerged []string) (bool, string, error)
	DeleteBranchFn          func(ctx context.Context, repoPath, branch string, force bool) (bool, string, error)
	ApplyStashFn            func(ctx context.Context, repoPath string, index int) (bool, string, error)
	DropStashFn             func(ctx context.Context, repoPath string, index int) (bool, string, error)
	BlameFn                 func(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)
}

MockOperations is a test double implementing Operations via injectable function fields.

func (*MockOperations) ApplyStash

func (m *MockOperations) ApplyStash(ctx context.Context, repoPath string, index int) (bool, string, error)

ApplyStash implements Operations.

func (*MockOperations) Blame added in v0.14.0

func (m *MockOperations) Blame(
	ctx context.Context, repoPath, path string, ranges []LineRange,
) ([]BlameLine, error)

Blame implements Operations.

func (*MockOperations) CleanupMergedBranches

func (m *MockOperations) CleanupMergedBranches(
	ctx context.Context, repoPath string, squashMerged []string,
) (bool, string, error)

CleanupMergedBranches implements Operations.

func (*MockOperations) CompareBranches

func (m *MockOperations) CompareBranches(ctx context.Context, repoPath, branch, target string) (int, int, error)

CompareBranches implements Operations.

func (*MockOperations) DeleteBranch

func (m *MockOperations) DeleteBranch(
	ctx context.Context, repoPath, branch string, force bool,
) (bool, string, error)

DeleteBranch implements Operations.

func (*MockOperations) DropStash

func (m *MockOperations) DropStash(ctx context.Context, repoPath string, index int) (bool, string, error)

DropStash implements Operations.

func (*MockOperations) FetchAll

func (m *MockOperations) FetchAll(ctx context.Context, repoPath string) (bool, string, error)

FetchAll implements Operations.

func (*MockOperations) GetAheadBehind

func (m *MockOperations) GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (int, int, error)

GetAheadBehind implements Operations.

func (*MockOperations) GetBranchList

func (m *MockOperations) GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)

GetBranchList implements Operations.

func (*MockOperations) GetCommitLog

func (m *MockOperations) GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)

GetCommitLog implements Operations.

func (*MockOperations) GetCurrentBranch

func (m *MockOperations) GetCurrentBranch(ctx context.Context, repoPath string) (string, error)

GetCurrentBranch implements Operations.

func (*MockOperations) GetLastModified

func (m *MockOperations) GetLastModified(ctx context.Context, repoPath string) (int64, error)

GetLastModified implements Operations.

func (*MockOperations) GetNewestModifiedFile

func (m *MockOperations) GetNewestModifiedFile(ctx context.Context, repoPath string) (string, time.Time, error)

GetNewestModifiedFile implements Operations.

func (*MockOperations) GetRemoteURL

func (m *MockOperations) GetRemoteURL(ctx context.Context, repoPath string) (string, error)

GetRemoteURL implements Operations.

func (*MockOperations) GetRepoSummary

func (m *MockOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)

GetRepoSummary implements Operations.

func (*MockOperations) GetStashList

func (m *MockOperations) GetStashList(ctx context.Context, repoPath string) ([]StashDetail, error)

GetStashList implements Operations.

func (*MockOperations) GetUpstream

func (m *MockOperations) GetUpstream(ctx context.Context, repoPath, branch string) (string, error)

GetUpstream implements Operations.

func (*MockOperations) GetWorktreeList

func (m *MockOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)

GetWorktreeList implements Operations.

func (*MockOperations) PruneRemote

func (m *MockOperations) PruneRemote(ctx context.Context, repoPath string) (bool, string, error)

PruneRemote implements Operations.

func (*MockOperations) PushBranch

func (m *MockOperations) PushBranch(
	ctx context.Context, repoPath, branch string, setUpstream bool,
) (bool, string, error)

PushBranch implements Operations.

func (*MockOperations) SwitchBranch

func (m *MockOperations) SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)

SwitchBranch implements Operations.

func (*MockOperations) VCSType

func (m *MockOperations) VCSType() Type

VCSType implements Operations.

type Mutator

type Mutator interface {
	// ApplyStash restores a stash's changes into the working copy without
	// removing it, so a mistaken apply costs nothing.
	ApplyStash(ctx context.Context, repoPath string, index int) (bool, string, error)
	// CleanupMergedBranches deletes local branches fully merged into the
	// default branch, plus any names in squashMerged: branches the caller has
	// already verified (via merged PR head OIDs) as squash-merged, which
	// `git branch --merged`/`jj bookmark` can't detect on their own.
	CleanupMergedBranches(ctx context.Context, repoPath string, squashMerged []string) (bool, string, error)
	// DeleteBranch removes one local branch. It refuses a branch that is not
	// fully merged unless force is set, which only a caller that has verified
	// the branch is squash-merged should pass.
	DeleteBranch(ctx context.Context, repoPath, branch string, force bool) (bool, string, error)
	// DropStash discards a stash. Nothing recovers it, so callers confirm first.
	DropStash(ctx context.Context, repoPath string, index int) (bool, string, error)
	FetchAll(ctx context.Context, repoPath string) (bool, string, error)
	// PushBranch pushes branch to origin along with the tags reachable from
	// it. setUpstream records the tracking link for a branch that has none.
	PushBranch(ctx context.Context, repoPath, branch string, setUpstream bool) (bool, string, error)
	PruneRemote(ctx context.Context, repoPath string) (bool, string, error)
	// SwitchBranch moves the working copy onto branch. It fails rather than
	// carrying or discarding uncommitted changes.
	SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)
}

Mutator performs write operations against a repository. Each method returns (success, message) alongside an error so callers can surface per-repo feedback in the UI even when the operation itself didn't error.

type Operations

type Operations interface {
	StatusReader
	DetailReader
	Mutator
}

Operations abstracts the git/jj commands used to inspect and mutate a repository.

func GetOperations

func GetOperations(repoPath string) Operations

GetOperations returns the Operations implementation matching repoPath's VCS type.

type RemoteBranchLister added in v0.7.0

type RemoteBranchLister interface {
	RemoteBranches(ctx context.Context, repoPath string) ([]string, error)
}

RemoteBranchLister names the branches a remote holds. A caller dispatching work against a ref needs those rather than the local checkout's, since a branch nobody checked out here is still a valid target.

type RepoStatus

type RepoStatus int

RepoStatus is a checkout's position relative to its upstream, or the state of its working tree when it has none.

const (
	RepoStatusClean RepoStatus = iota
	RepoStatusDirty
	RepoStatusAhead
	RepoStatusBehind
	RepoStatusDiverged
)

RepoStatus values.

func (RepoStatus) String

func (r RepoStatus) String() string

type RepoSummary

type RepoSummary struct {
	LastModified    time.Time
	ParentPath      string
	Branch          string
	Upstream        string
	Path            string
	RemoteID        string
	RemoteRepo      string
	RemoteProtocol  string
	ConfigOverrides []GitConfigOverride
	Ahead           int
	StashCount      int
	Conflicted      int
	Untracked       int
	Unstaged        int
	Staged          int
	Behind          int
	VCSType         Type
	NoCommits       bool
}

RepoSummary is what a checkout says about itself: its branch, its position against its upstream, and the state of its working tree.

func (RepoSummary) DirtyLabel

func (r RepoSummary) DirtyLabel() string

DirtyLabel names why IsDirty is true, since uncommitted files and unpushed commits need different work to resolve. Empty when the repo is neither.

func (RepoSummary) HasConfigOverrides

func (r RepoSummary) HasConfigOverrides() bool

HasConfigOverrides reports whether the repo has any local git config value that differs from the same key's global value.

func (RepoSummary) IsDetached

func (r RepoSummary) IsDetached() bool

IsDetached reports whether the repo's HEAD points at a commit rather than a branch.

func (RepoSummary) IsDirty

func (r RepoSummary) IsDirty() bool

IsDirty reports whether the repo has uncommitted changes or unpushed commits.

func (RepoSummary) IsLinkedCheckout

func (r RepoSummary) IsLinkedCheckout() bool

IsLinkedCheckout reports whether the repo is a git worktree or jj workspace of another checkout rather than a standalone clone.

func (RepoSummary) Name

func (r RepoSummary) Name() string

Name returns the checkout's directory name.

func (RepoSummary) Status

func (r RepoSummary) Status() RepoStatus

Status returns the repo's overall RepoStatus.

func (RepoSummary) UncommittedCount

func (r RepoSummary) UncommittedCount() int

UncommittedCount returns the total number of staged, unstaged, untracked, and conflicted files.

type StashDetail

type StashDetail struct {
	Date    time.Time
	Message string
	Branch  string
	Index   int
}

StashDetail summarizes a single stash entry.

type StatusReader

type StatusReader interface {
	CompareBranches(ctx context.Context, repoPath, branch, target string) (ahead, behind int, err error)
	GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (ahead, behind int, err error)
	GetCurrentBranch(ctx context.Context, repoPath string) (string, error)
	GetLastModified(ctx context.Context, repoPath string) (int64, error)
	GetRemoteURL(ctx context.Context, repoPath string) (string, error)
	GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)
	GetUpstream(ctx context.Context, repoPath, branch string) (string, error)
	VCSType() Type
}

StatusReader answers summary-level queries about a repository's current state.

type Type

type Type int

Type identifies the version control system managing a repo.

const (
	TypeGit Type = iota
	TypeJJ
)

Type values.

func DetectVCSType

func DetectVCSType(repoPath string) Type

DetectVCSType inspects repoPath to determine whether it is a jj or git repository. A colocated repo (both .jj and .git) is treated as git when the jj binary is not on PATH, so repos stay usable instead of erroring on every jj call.

func (Type) String

func (v Type) String() string

type WorktreeInfo

type WorktreeInfo struct {
	Path     string
	Branch   string
	IsBare   bool
	IsLocked bool
}

WorktreeInfo summarizes a single git worktree.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL