Documentation
¶
Overview ¶
Package vcs inspects an existing checkout: which version control system drives it, and where the repository containing a given path begins. Both answers come from marker files alone - no subprocess is spawned and no repository is opened - so this package is as pure and offline as the reference parsing in the parent package.
Driving a VCS is deliberately out of scope. Running git or jj belongs to the consumer, whose needs vary too widely (progress reporting, dry-run rendering, signing flags) for a shared primitive to serve without either crippling one caller or dragging os/exec into this module.
Index ¶
Constants ¶
const ( Git = "git" Hg = "hg" JJ = "jj" SVN = "svn" )
The version control systems this package recognizes.
Variables ¶
This section is empty.
Functions ¶
func Detect ¶
Detect reports which VCS drives the checkout rooted at dir - JJ, Git, or "" when neither marker is present. Only dir itself is examined; use a Resolver to find the root a nested path belongs to.
Mercurial and Subversion checkouts report "", not Hg or SVN: Detect answers "what should I drive this with", and this module's consumers drive only git and jj.
An empty dir reports "" rather than inspecting the process working directory, which is what joining a marker onto "" would otherwise do. That case is not hypothetical: it is exactly what pairing Detect with a Resolver produces for a path in no repository, and the wrong answer would depend on where the process happened to be started. Prefer Resolver.RootVCS, which pairs them safely.
Types ¶
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver maps a path to the root of the repository it belongs to, caching each directory it resolves so an ancestor is checked at most once. The root is the namespace under which a per-repository identifier is unique, which is what makes the same name in two checkouts not a clash. Safe for concurrent use.
Paths are canonicalised before they are walked, so one repository has one root however a caller spells the way to it - relative or absolute, through a symlink or through the real path. Without that a single repository would yield two namespaces and two identifiers that should collide would not, which is a failure that reports nothing.
func NewResolver ¶
func NewResolver() *Resolver
NewResolver returns an empty resolver. It captures the working directory once - callers hand it relative paths, and filepath.Abs would otherwise re-issue the getwd syscall for every lookup of a value that never changes during a run.
func (*Resolver) Abs ¶
Abs returns path as this resolver keys on it: absolute, cleaned, and with symlinks resolved.
Use it in place of filepath.Abs when relating a file to its repository, so that the two agree:
rel, err := filepath.Rel(r.Root(p), r.Abs(p))
filepath.Abs does not resolve symlinks, so a path reaching a repository through one does not sit under the root it resolves to and Rel yields a "../"-escaping result instead of a path within the repository.
func (*Resolver) Root ¶
Root returns the absolute path of the repository the file at path belongs to - the nearest ancestor directory holding any marker in Markers - or "" when the file is not inside a repository. The file itself need not exist.
func (*Resolver) RootDir ¶
RootDir returns the absolute path of the repository containing the directory dir - the nearest ancestor, dir itself included, holding a marker - or "" when dir is not inside a repository. Unlike Root, dir is the search start rather than a file whose parent is searched, so a repository root resolves to itself.
func (*Resolver) RootVCS ¶
RootVCS returns the repository containing dir and which VCS drives it, or ("", "") when dir is in no repository.
It exists because the obvious pairing is a trap: RootDir yields "" outside a repository, and joining a marker onto "" would inspect the process working directory instead, reporting whatever VCS happens to govern where the program was started. Detect guards that case for exactly this reason, so the pairing here is a single expression - but a caller writing it by hand has to know that, and three consumers should not each have to rediscover it.
The two answers can disagree about which VCS "owns" a colocated checkout: Detect prefers jj because it drives the working copy. A caller wanting a different policy - git because it needs no jj binary, say - has the root here and can decide for itself.