git

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package git provides helpers for interacting with git repositories, focused on worktree and branch management for Clank's session isolation.

All functions shell out to the git CLI rather than using a Go git library. This keeps the dependency footprint small and ensures exact behavioral parity with the user's installed git version.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrPushNotFastForward fires when the remote branch has commits
	// the local branch doesn't. Callers should surface a 409 with a
	// "rebase or force-push" hint; v1 doesn't auto-force.
	ErrPushNotFastForward = errors.New("git push: not a fast-forward")

	// ErrPushRepoNotFound fires when the remote URL is reachable but
	// the repo isn't (404 from GitHub). GitHub deliberately returns
	// "Repository not found" for both 404 and 403 (private repos
	// without access) so an attacker can't enumerate.
	ErrPushRepoNotFound = errors.New("git push: repository not found or no access")

	// ErrPushPermissionDenied fires for explicit auth failures (e.g.
	// 401 from the remote). Distinct from ErrPushRepoNotFound — this
	// one means "we found the repo but our token can't write".
	ErrPushPermissionDenied = errors.New("git push: permission denied")
)

Push errors that callers map to specific HTTP statuses or UI messages. Anything else surfaces via the wrapping error from Push.

View Source
var ErrMergeConflict = errors.New("git merge: conflicts")

ErrMergeConflict is returned by Merge when the merge stops on conflicts. The repository is left mid-merge (MERGE_HEAD present) so the caller can inspect ConflictedFiles, hand resolution to an agent, or AbortMerge.

View Source
var ErrNothingToCommit = fmt.Errorf("nothing to commit")

ErrNothingToCommit is returned by Commit when there are no staged changes.

Functions

func AbortMerge

func AbortMerge(dir string) error

AbortMerge aborts an in-progress merge.

func AddAll

func AddAll(dir string) error

AddAll stages all changes (tracked and untracked) in the working tree at dir. Equivalent to `git add -A`.

func AddWorktree

func AddWorktree(repoDir, worktreeDir, branch string) error

AddWorktree creates a new worktree for an existing branch.

func AddWorktreeNewBranch

func AddWorktreeNewBranch(repoDir, worktreeDir, branch, base string) error

AddWorktreeNewBranch creates a new worktree with a new branch based on the given base ref.

func AheadBehind

func AheadBehind(dir, local, remote string) (ahead, behind int, err error)

AheadBehind returns how many commits local has that remote lacks (ahead) and how many remote has that local lacks (behind), via `git rev-list --left-right --count local...remote`. Both revisions must resolve in dir's object store; an unknown revision returns an error (callers treat that as "remote advanced past a commit we don't have").

func BackupRef

func BackupRef(dir, name, target string) error

BackupRef points the fully-qualified ref name at target (typically HEAD before a destructive reset), keeping discarded work recoverable. name must be fully qualified, e.g. "refs/clank/backup/<branch>-<sha>".

func BranchExists

func BranchExists(dir, branch string) (bool, error)

BranchExists returns true if the given branch exists locally.

func Checkout

func Checkout(dir, branch string) error

Checkout switches the working tree at dir to the given branch.

func CloneBare

func CloneBare(ctx context.Context, url, gitDir, token, branch, credentialHelper string) error

func CloneShallowKeepRemote

func CloneShallowKeepRemote(ctx context.Context, url, dir, token, branch string) error

CloneShallowKeepRemote shallow-clones url into dir (--depth 1) keeping the .git directory and the origin remote — for importing a user's existing repo, where history depth doesn't matter but the link back to the remote does. token authenticates HTTPS clones of private repos; pass "" for public repos. branch checks out a specific branch (empty clones the remote's default). dir must not already exist.

The token reaches git through cloneTokenEnv + an inline credential helper rather than the URL, so it appears neither in argv nor in the resulting .git/config. The leading empty credential.helper resets any system/global helper so it can't shadow ours.

func Commit

func Commit(dir, message string) error

Commit creates a commit in the repository at dir with the given message. Returns ErrNothingToCommit if the working tree is clean (nothing staged).

func CommitLog

func CommitLog(dir, base, branch string) (string, error)

CommitLog returns a one-line-per-commit log of commits on branch that are not on base. Format: "<short-hash> <subject>". Newest first.

func CommitsAhead

func CommitsAhead(dir, base, branch string) (int, error)

CommitsAhead returns the number of commits that branch has ahead of base. For example, CommitsAhead(dir, "main", "feat/login") returns how many commits feat/login has that main does not.

func CommonDir

func CommonDir(dir string) (string, error)

CommonDir returns the repo's shared git directory (absolute): a non-bare repo's `.git`, a linked worktree's parent repo git dir, or a bare repo's own path. The primitive under MainWorktreeRoot, exposed for callers that want the git dir itself (e.g. resolving which canonical a linked worktree belongs to).

func ConflictedFiles

func ConflictedFiles(dir string) ([]string, error)

ConflictedFiles returns the working-tree paths with unresolved merge conflicts (the "U" entries of `git diff --diff-filter=U`). Empty when there are none.

func CurrentBranch

func CurrentBranch(dir string) (string, error)

CurrentBranch returns the currently checked-out branch in dir. Returns "HEAD" if in detached HEAD state.

func DefaultBranch

func DefaultBranch(dir string) (string, error)

DefaultBranch returns the default branch name for the repository containing dir. It checks for refs/heads/main first, then refs/heads/master, then falls back to whatever HEAD points to on origin, and finally returns "main" as a last resort.

func DeleteBranch

func DeleteBranch(dir, branch string, force bool) error

DeleteBranch deletes a local branch. If force is true, uses -D (force delete) instead of -d (safe delete, requires branch to be fully merged).

func DiffStat

func DiffStat(worktreeDir, base string) (added, removed int, err error)

DiffStat returns the total lines added and removed in a worktree compared to the given base branch. This includes both staged and unstaged changes. The base is typically the repo's default branch (e.g. "main").

func Fetch

func Fetch(dir, remote, ref string, opts PushOptions) error

Fetch refreshes one ref from the named remote, using the same process-local auth pattern as Push. Used by callers that need origin/<base> to exist before running a local diff against it (e.g. host.Service.CreatePR's commits-ahead check on a sprite whose worktree was migrated piecemeal without main ever being fetched).

Errors are returned verbatim; callers decide whether a fetch failure is fatal. A common pattern is to log + continue, since the downstream check has its own fallback.

func FetchBundleObjects

func FetchBundleObjects(dir, bundlePath string) error

FetchBundleObjects loads the git objects from the bundle file at bundlePath into the repo at dir's object store, without moving any branch or touching the working tree. Afterwards, commits contained in the bundle (e.g. a remote HEAD) are available for ancestry checks before any destructive apply.

func GetLocalConfig

func GetLocalConfig(dir, key string) (string, error)

GetLocalConfig reads a repository-local git config value from the repo at dir. Returns ("", nil) when the key is unset — the read-side counterpart of SetLocalConfig.

func HasStagedChanges

func HasStagedChanges(dir string) (bool, error)

HasStagedChanges returns true if there are staged changes ready to commit. This checks the index against HEAD, ignoring unstaged and untracked files.

func HeadBranch

func HeadBranch(dir string) (string, error)

HeadBranch returns the branch HEAD symbolically points at in the repo at dir. This is the canonical's default branch — DefaultBranch's main/master probing is wrong for single-branch clones of a repo whose default is neither. Errors on a detached HEAD.

func HeadCommit

func HeadCommit(dir string) (string, error)

HeadCommit returns the full SHA of HEAD in dir.

func Init

func Init(ctx context.Context, dir, defaultBranch string) error

Init initializes a new git repository at dir with defaultBranch as the initial branch (git init -b <branch>). dir is created if needed.

func InitBare

func InitBare(ctx context.Context, gitDir, defaultBranch string) error

InitBare creates an empty bare repository at gitDir with HEAD pointing at defaultBranch — the greenfield canonical, which gets its first commit via a filesystem push from the scaffold's temp checkout.

func IsAncestor

func IsAncestor(dir, ancestor, descendant string) (bool, error)

IsAncestor reports whether commit `ancestor` is an ancestor of `descendant` (i.e. descendant is reachable from ancestor) in the repo at dir. Both commits must already be present in the local object store. Used to gate a fast-forward pull.

func IsClean

func IsClean(dir string) (bool, error)

IsClean returns true if the working tree at dir has no uncommitted changes (no staged, unstaged, or untracked files). Used to verify the main worktree is safe to merge into.

func IsMerging

func IsMerging(dir string) bool

IsMerging returns true if the repository at dir is in the middle of a merge (i.e., MERGE_HEAD exists). This is useful to detect conflicts after a failed merge.

func IsRemoteNotConfigured

func IsRemoteNotConfigured(err error) bool

IsRemoteNotConfigured reports whether err came from RemoteURL finding no such remote configured (git config --get exits 1), as opposed to a real git/filesystem failure (corrupt config, missing repo, etc.) that happens to occur on the same call — those must not be treated as "no remote".

func LocalBranches

func LocalBranches(dir string) ([]string, error)

LocalBranches returns the list of local branch names in the repository containing dir.

func LsRemoteDefaultBranch

func LsRemoteDefaultBranch(ctx context.Context, url string) (string, error)

LsRemoteDefaultBranch asks a remote for the branch its HEAD points at, deliberately without credentials: `-c credential.helper=` resets any configured helpers and GIT_TERMINAL_PROMPT=0 fails fast instead of prompting, so a successful answer proves the URL is publicly clonable. Unlike GitHub's REST API, anonymous git smart-HTTP is not subject to the tiny unauthenticated per-IP rate limit.

func MainWorktreeRoot

func MainWorktreeRoot(dir string) (string, error)

MainWorktreeRoot returns the top-level directory of the *main* worktree for the repo containing dir. Inside a `git worktree add`-ed worktree, RepoRoot returns the worktree's own path; this helper follows the shared `.git` to find the parent repo. Used when we need to derive a stable "project root" for naming purposes (e.g. the `~/.clank/worktrees/<project>/<branch>/` convention).

func Merge

func Merge(dir, ref, message string) error

Merge merges ref into the current branch — fast-forwarding when possible, otherwise creating a merge commit with message. Returns ErrMergeConflict when the merge stops on conflicts; the repo is left mid-merge for the caller to resolve (ConflictedFiles) or AbortMerge.

func MergeBase

func MergeBase(dir, ref1, ref2 string) (string, error)

MergeBase returns the best common ancestor of ref1 and ref2, or an empty string when none exists (git exits 1). A real error is only returned for unexpected failures. Used as a safety check before pushing to a remote: an empty result means our branch has no shared history with the remote's base, which almost always means the remote points at the wrong repo.

func MergeFF

func MergeFF(dir, ref string) error

MergeFF fast-forwards the current branch to ref. Fails rather than creating a merge commit when the branch can't be fast-forwarded (local has commits ref lacks). Gate with IsAncestor(HEAD, ref) first.

func MergeNoFF

func MergeNoFF(dir, branch, message string) error

MergeNoFF merges the given branch into the current branch using --no-ff (always creates a merge commit). The message is the commit message for the merge commit. Returns nil on success, an error if conflicts occur or the merge fails for another reason.

func PruneWorktrees

func PruneWorktrees(dir string) error

PruneWorktrees runs `git worktree prune` in the repo at dir, dropping bookkeeping for worktree dirs that no longer exist on disk (manual rm, failed adds).

func Push

func Push(dir, remote, refspec string, opts PushOptions) error

Push runs `git push` against the configured remote with the given refspec. Returns one of the typed errors above when the failure is classifiable, otherwise wraps git's stderr.

The auth header is passed via process-internal git config (-c http.extraheader=...) so it never persists to disk. It does land in `ps` output for the duration of the subprocess; we accept that — the sprite is single-user and the token is base64-encoded inside the header value.

func RemoteAdd

func RemoteAdd(dir, name, url string) error

RemoteAdd runs `git remote add <name> <url>` in dir — used when publishing a previously remote-less worktree to a fresh GitHub repo. Errors if the remote already exists.

func RemoteTrackingBranchExists

func RemoteTrackingBranchExists(dir, remote, branch string) (bool, error)

RemoteTrackingBranchExists reports whether refs/remotes/<remote>/<branch> resolves in dir. (BranchExists checks refs/heads only.) Used to guard ahead/behind computations that need a tracking ref to compare against.

func RemoteURL

func RemoteURL(dir, remote string) (string, error)

RemoteURL returns the URL of the named remote (typically "origin") for the repository containing dir. Returns an error when the remote is not configured — callers must decide whether to treat that as fatal or degrade gracefully (a brand-new local repo may have no remotes yet).

func RemoteURLs

func RemoteURLs(dir string) (map[string]string, error)

RemoteURLs returns every configured remote's URL, keyed by remote name. Used by the host's CreateSession when a caller passes Dir: any remote whose canonical form matches the requested GitRef counts as a valid match (people fork-then-add-upstream all the time).

func RemoveWorktree

func RemoveWorktree(repoDir, worktreeDir string, force bool) error

RemoveWorktree removes a worktree. If force is true, uses --force to remove even if the worktree has uncommitted changes.

func RepoRoot

func RepoRoot(dir string) (string, error)

RepoRoot returns the top-level directory of the git repository containing dir.

func ResetHard

func ResetHard(dir, ref string) error

ResetHard moves the current branch and working tree to ref, discarding local commits and uncommitted changes not reachable from ref. Destructive — callers should BackupRef the prior HEAD first when the discarded work might be wanted back.

func RevParse

func RevParse(dir, ref string) (string, error)

RevParse resolves ref (a branch, tag, or revision expression such as FETCH_HEAD) to a full commit SHA in dir.

func SetLocalConfig

func SetLocalConfig(dir, key, value string) error

SetLocalConfig sets a repository-local git config value (git config <key> <value>) in the repo at dir. Used to give a freshly-initialised project a committer identity so the initial commit doesn't depend on global git config being present on the host.

func WorkingTreeDirty

func WorkingTreeDirty(dir string) (bool, error)

WorkingTreeDirty reports whether the working tree at dir has any uncommitted changes, INCLUDING untracked (but not gitignored) files. Use this for surfacing "you have local work" to the user; for "is it safe to fast-forward" use IsClean, which ignores untracked files git preserves across a merge.

func WorktreeDir

func WorktreeDir(projectName, branch string) (string, error)

WorktreeDir returns the conventional Clank worktree directory path for a branch. Convention: ~/.clank/worktrees/<project-name>/<sanitized-branch>/

Types

type BranchTip

type BranchTip struct {
	Branch      string
	SHA         string
	CommittedAt time.Time
}

BranchTip is one refs/heads entry: the branch, its tip SHA, and the tip commit's committer time.

func LocalBranchTips

func LocalBranchTips(dir string) ([]BranchTip, error)

LocalBranchTips returns every refs/heads/* tip via a single for-each-ref invocation — the repo overview's git half without N× `git log`. Ordered most recently committed first.

type PushOptions

type PushOptions struct {
	// ExtraHeader is appended to every HTTP request git makes during
	// this push. Empty value means no header is sent (the default).
	ExtraHeader string
}

PushOptions controls how Push invokes git. ExtraHeader, when set, is passed via `-c http.extraheader=<value>` so the credential never lands in `.git/config`, the remote URL, or `ps` output. Callers build the header value (typically "Authorization: Basic <b64(x-access-token:tok)>") themselves — this package stays agnostic to credential format.

type Worktree

type Worktree struct {
	Path   string // Absolute filesystem path of the worktree
	Branch string // Branch checked out in this worktree (short name, e.g. "main")
	Bare   bool   // True if this is the bare repository entry
	Head   string // HEAD commit hash
}

Worktree represents a git worktree entry as returned by `git worktree list --porcelain`.

func FindWorktreeForBranch

func FindWorktreeForBranch(dir, branch string) (*Worktree, error)

FindWorktreeForBranch returns the worktree that has the given branch checked out, or nil if no worktree exists for that branch.

func ListWorktrees

func ListWorktrees(dir string) ([]Worktree, error)

ListWorktrees returns all worktrees for the repository containing dir.

Jump to

Keyboard shortcuts

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