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 ¶
- Variables
- func BranchCacheKey(repoPath string) string
- func CheckoutIdentity(repoPath string) string
- func CommitCacheKey(repoPath string, count int) string
- func DefaultBranchName(ctx context.Context, repoPath string) (string, bool)
- func DetachedBranchLabel(shortHash string) string
- func ExtractRepoPath(remoteURL string) string
- func GetGitHubEnv(repoPath string) []string
- func HeadSHA(ctx context.Context, repoPath string) (string, error)
- func IsDefaultBranchName(name string) bool
- func IsDetachedBranch(label string) bool
- func IsRepo(path string) bool
- func PullFastForward(ctx context.Context, repoPath string) error
- func RemoteBranches(ctx context.Context, repoPath string) ([]string, error)
- func RemoteIdentity(remoteURL string) string
- func RemoteIdentityFor(ctx context.Context, repoPath string) string
- func ResetHardToUpstream(ctx context.Context, repoPath string) error
- func SetExternalDiffCommand(command string)
- func Stamp(repoPath string) cache.Stamp
- type BlameLine
- type BranchInfo
- type CommitInfo
- type DefaultBranch
- type DefaultBranchResolver
- type DetailReader
- type GitConfigOverride
- type GitOperations
- func (g *GitOperations) ApplyStash(ctx context.Context, repoPath string, index int) (bool, string, error)
- func (g *GitOperations) Blame(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)
- func (g *GitOperations) CleanupMergedBranches(ctx context.Context, repoPath string, squashMerged []string) (bool, string, error)
- func (g *GitOperations) CompareBranches(ctx context.Context, repoPath, branch, target string) (int, int, error)
- func (g *GitOperations) DeleteBranch(ctx context.Context, repoPath, branch string, force bool) (bool, string, error)
- func (g *GitOperations) DropStash(ctx context.Context, repoPath string, index int) (bool, string, error)
- func (g *GitOperations) ExternalDiffCommand(ctx context.Context, repoPath string) string
- func (g *GitOperations) FetchAll(ctx context.Context, repoPath string) (bool, string, error)
- func (g *GitOperations) GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (int, int, error)
- func (g *GitOperations) GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)
- func (g *GitOperations) GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)
- func (g *GitOperations) GetConflictedCount(ctx context.Context, repoPath string) (int, error)
- func (g *GitOperations) GetCurrentBranch(ctx context.Context, repoPath string) (string, error)
- func (g *GitOperations) GetLastModified(ctx context.Context, repoPath string) (int64, error)
- func (g *GitOperations) GetNewestModifiedFile(ctx context.Context, repoPath string) (string, time.Time, error)
- func (g *GitOperations) GetRemoteURL(ctx context.Context, repoPath string) (string, error)
- func (g *GitOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)
- func (g *GitOperations) GetStagedCount(ctx context.Context, repoPath string) (int, error)
- func (g *GitOperations) GetStashList(ctx context.Context, repoPath string) ([]StashDetail, error)
- func (g *GitOperations) GetUnstagedCount(ctx context.Context, repoPath string) (int, error)
- func (g *GitOperations) GetUntrackedCount(ctx context.Context, repoPath string) (int, error)
- func (g *GitOperations) GetUpstream(ctx context.Context, repoPath, branch string) (string, error)
- func (g *GitOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)
- func (g *GitOperations) PreviewMergedBranches(ctx context.Context, repoPath string) (string, []string, error)
- func (g *GitOperations) PruneRemote(ctx context.Context, repoPath string) (bool, string, error)
- func (g *GitOperations) PushBranch(ctx context.Context, repoPath, branch string, setUpstream bool) (bool, string, error)
- func (g *GitOperations) RemoteBranches(ctx context.Context, repoPath string) ([]string, error)
- func (g *GitOperations) ResolveDefaultBranch(ctx context.Context, repoPath string) (string, bool)
- func (g *GitOperations) StashDiff(ctx context.Context, repoPath string, index int) (string, error)
- func (*GitOperations) StashDiffExternal(ctx context.Context, repoPath string, index, width int, command string) (string, error)
- func (g *GitOperations) StashDiffstat(ctx context.Context, repoPath string, index int) (string, error)
- func (g *GitOperations) SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)
- func (g *GitOperations) UncommittedDiff(ctx context.Context, repoPath string) (string, error)
- func (*GitOperations) UncommittedDiffExternal(ctx context.Context, repoPath string, width int, command string) (string, error)
- func (g *GitOperations) UncommittedDiffstat(ctx context.Context, repoPath string) (string, error)
- func (*GitOperations) VCSType() Type
- type JJOperations
- func (*JJOperations) ApplyStash(_ context.Context, _ string, _ int) (bool, string, error)
- func (j *JJOperations) Blame(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)
- func (j *JJOperations) CleanupMergedBranches(ctx context.Context, repoPath string, squashMerged []string) (bool, string, error)
- func (j *JJOperations) CompareBranches(ctx context.Context, repoPath, branch, target string) (int, int, error)
- func (j *JJOperations) DeleteBranch(ctx context.Context, repoPath, branch string, _ bool) (bool, string, error)
- func (*JJOperations) DropStash(_ context.Context, _ string, _ int) (bool, string, error)
- func (j *JJOperations) FetchAll(ctx context.Context, repoPath string) (bool, string, error)
- func (j *JJOperations) GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (int, int, error)
- func (j *JJOperations) GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)
- func (j *JJOperations) GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)
- func (*JJOperations) GetConflictedCount(_ context.Context, _ string) (int, error)
- func (j *JJOperations) GetCurrentBranch(ctx context.Context, repoPath string) (string, error)
- func (j *JJOperations) GetLastModified(ctx context.Context, repoPath string) (int64, error)
- func (*JJOperations) GetNewestModifiedFile(_ context.Context, _ string) (string, time.Time, error)
- func (j *JJOperations) GetRemoteURL(ctx context.Context, repoPath string) (string, error)
- func (j *JJOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)
- func (*JJOperations) GetStagedCount(_ context.Context, _ string) (int, error)
- func (*JJOperations) GetStashList(_ context.Context, _ string) ([]StashDetail, error)
- func (j *JJOperations) GetUnstagedCount(ctx context.Context, repoPath string) (int, error)
- func (*JJOperations) GetUntrackedCount(_ context.Context, _ string) (int, error)
- func (j *JJOperations) GetUpstream(ctx context.Context, repoPath, branch string) (string, error)
- func (j *JJOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)
- func (j *JJOperations) PreviewMergedBranches(ctx context.Context, repoPath string) (string, []string, error)
- func (*JJOperations) PruneRemote(_ context.Context, _ string) (bool, string, error)
- func (j *JJOperations) PushBranch(ctx context.Context, repoPath, branch string, setUpstream bool) (bool, string, error)
- func (j *JJOperations) RemoteBranches(ctx context.Context, repoPath string) ([]string, error)
- func (j *JJOperations) ResolveDefaultBranch(ctx context.Context, repoPath string) (string, bool)
- func (j *JJOperations) SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)
- func (*JJOperations) VCSType() Type
- type LineRange
- type MockOperations
- func (m *MockOperations) ApplyStash(ctx context.Context, repoPath string, index int) (bool, string, error)
- func (m *MockOperations) Blame(ctx context.Context, repoPath, path string, ranges []LineRange) ([]BlameLine, error)
- func (m *MockOperations) CleanupMergedBranches(ctx context.Context, repoPath string, squashMerged []string) (bool, string, error)
- func (m *MockOperations) CompareBranches(ctx context.Context, repoPath, branch, target string) (int, int, error)
- func (m *MockOperations) DeleteBranch(ctx context.Context, repoPath, branch string, force bool) (bool, string, error)
- func (m *MockOperations) DropStash(ctx context.Context, repoPath string, index int) (bool, string, error)
- func (m *MockOperations) FetchAll(ctx context.Context, repoPath string) (bool, string, error)
- func (m *MockOperations) GetAheadBehind(ctx context.Context, repoPath, branch, upstream string) (int, int, error)
- func (m *MockOperations) GetBranchList(ctx context.Context, repoPath string) ([]BranchInfo, error)
- func (m *MockOperations) GetCommitLog(ctx context.Context, repoPath string, count int) ([]CommitInfo, error)
- func (m *MockOperations) GetCurrentBranch(ctx context.Context, repoPath string) (string, error)
- func (m *MockOperations) GetLastModified(ctx context.Context, repoPath string) (int64, error)
- func (m *MockOperations) GetNewestModifiedFile(ctx context.Context, repoPath string) (string, time.Time, error)
- func (m *MockOperations) GetRemoteURL(ctx context.Context, repoPath string) (string, error)
- func (m *MockOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)
- func (m *MockOperations) GetStashList(ctx context.Context, repoPath string) ([]StashDetail, error)
- func (m *MockOperations) GetUpstream(ctx context.Context, repoPath, branch string) (string, error)
- func (m *MockOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)
- func (m *MockOperations) PruneRemote(ctx context.Context, repoPath string) (bool, string, error)
- func (m *MockOperations) PushBranch(ctx context.Context, repoPath, branch string, setUpstream bool) (bool, string, error)
- func (m *MockOperations) SwitchBranch(ctx context.Context, repoPath, branch string) (bool, string, error)
- func (m *MockOperations) VCSType() Type
- type Mutator
- type Operations
- type RemoteBranchLister
- type RepoStatus
- type RepoSummary
- func (r RepoSummary) DirtyLabel() string
- func (r RepoSummary) HasConfigOverrides() bool
- func (r RepoSummary) IsDetached() bool
- func (r RepoSummary) IsDirty() bool
- func (r RepoSummary) IsLinkedCheckout() bool
- func (r RepoSummary) Name() string
- func (r RepoSummary) Status() RepoStatus
- func (r RepoSummary) UncommittedCount() int
- type StashDetail
- type StatusReader
- type Type
- type WorktreeInfo
Constants ¶
This section is empty.
Variables ¶
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.
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.
var ErrCommandFailed = errors.New("command failed")
ErrCommandFailed wraps a non-zero exit from the underlying git/jj CLI.
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.
var ErrUnexpectedOutput = errors.New("unexpected command output")
ErrUnexpectedOutput wraps a CLI output that didn't match the expected format.
Functions ¶
func BranchCacheKey ¶
BranchCacheKey builds the branch list cache key for a checkout.
func CheckoutIdentity ¶
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 ¶
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
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 ¶
DetachedBranchLabel formats a detached HEAD's short commit as the branch label shown wherever a branch name would go.
func ExtractRepoPath ¶
ExtractRepoPath derives an "owner/repo" style path from a git remote URL.
func GetGitHubEnv ¶
GetGitHubEnv returns extra environment variables needed for the gh CLI to work in a jj-colocated repo.
func HeadSHA ¶
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 ¶
IsDefaultBranchName reports whether name is one of the conventional default-branch names that cleanup should never treat as a feature branch.
func IsDetachedBranch ¶
IsDetachedBranch reports whether label came from DetachedBranchLabel, so views can say "detached" rather than treating the commit as a branch name.
func PullFastForward ¶
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
RemoteBranches names the branches the remote holds, sorted and deduplicated, with the "origin/" prefix and origin/HEAD dropped.
func RemoteIdentity ¶
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 ¶
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
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 ¶
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
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 ¶
CommitInfo summarizes a single commit.
type DefaultBranch ¶
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 ¶
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) 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 ¶
GetConflictedCount reports the number of files with merge conflicts.
func (*GitOperations) GetCurrentBranch ¶
GetCurrentBranch implements Operations.
func (*GitOperations) GetLastModified ¶
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 ¶
GetRemoteURL implements Operations.
func (*GitOperations) GetRepoSummary ¶
func (g *GitOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)
GetRepoSummary implements Operations.
func (*GitOperations) GetStagedCount ¶
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 ¶
GetUnstagedCount reports the number of unstaged, modified files.
func (*GitOperations) GetUntrackedCount ¶
GetUntrackedCount reports the number of untracked files.
func (*GitOperations) GetUpstream ¶
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 ¶
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
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
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 ¶
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 ¶
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 ¶
UncommittedDiffstat summarizes the working tree's changes against HEAD, for the focused view's detail pane once the operator asks past the full diff.
type JJOperations ¶
type JJOperations struct{}
JJOperations implements Operations for jj (Jujutsu) repositories.
func (*JJOperations) ApplyStash ¶
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) 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 ¶
GetConflictedCount always returns 0: conflict detection isn't implemented for jj.
func (*JJOperations) GetCurrentBranch ¶
GetCurrentBranch implements Operations.
func (*JJOperations) GetLastModified ¶
GetLastModified implements Operations.
func (*JJOperations) GetNewestModifiedFile ¶
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 ¶
GetRemoteURL implements Operations.
func (*JJOperations) GetRepoSummary ¶
func (j *JJOperations) GetRepoSummary(ctx context.Context, repoPath string) (RepoSummary, error)
GetRepoSummary implements Operations.
func (*JJOperations) GetStagedCount ¶
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 ¶
GetUnstagedCount reports the number of modified files in the working copy.
func (*JJOperations) GetUntrackedCount ¶
GetUntrackedCount always returns 0: jj automatically tracks all files.
func (*JJOperations) GetUpstream ¶
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 ¶
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
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
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.
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) 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 ¶
GetCurrentBranch implements Operations.
func (*MockOperations) GetLastModified ¶
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 ¶
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 ¶
GetUpstream implements Operations.
func (*MockOperations) GetWorktreeList ¶
func (m *MockOperations) GetWorktreeList(ctx context.Context, repoPath string) ([]WorktreeInfo, error)
GetWorktreeList implements Operations.
func (*MockOperations) PruneRemote ¶
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 ¶
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.
func DetectVCSType ¶
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.