git

package
v0.1.0-dev.20260822014012 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	Checkout op.ActionName = "git.checkout"
	Clone    op.ActionName = "git.clone"
	Observe  op.ActionName = "git.observe"
	Pull     op.ActionName = "git.pull"
)

Action-name constants for the git provider's plan-mode actions.

Each constant is the short dotted action label its method dispatches under. Pass these to plan.Plan, op.ReceiverRegistry().BuildAction, RuntimeEnvironment.ActionByName, or WithActionNamed in place of a string literal so a typo is a compile error and rename / find-references work through the constant.

Variables

This section is empty.

Functions

This section is empty.

Types

type Observation

type Observation struct {
	op.ObservationBase

	// ObservedHEAD is the commit SHA the on-disk clone currently points at. May differ from the
	// observed [Resource]'s `HEAD` (which is plan-time intent).
	ObservedHEAD string

	// ObservedRef is the branch / tag / ref name the on-disk clone is positioned at. May differ
	// from the observed [Resource]'s `Ref` (which is plan-time intent).
	ObservedRef string

	// Bare reports whether the on-disk repository is bare (no working tree).
	Bare bool

	// Dirty reports whether the working tree had uncommitted changes at observation time. Always
	// false for bare repositories.
	Dirty bool

	// Remotes maps remote name (e.g., `origin`) to the fetch / push URL pair recorded in
	// `.git/config` at observation time.
	Remotes map[string]Remote
}

Observation captures the runtime-observed state of a *Resource's on-disk clone at the moment it was observed.

Distinct from Resource, which carries identity (URI, fsroot.Path, and the identity-extension intent fields `HEAD` and `Ref` from the plan). An observation is a point-in-time metadata snapshot record — not a Resource, never cataloged — whose identity comes from the resource it references (op.ObservationBase.OfResource, by pointer value). It embeds op.ObservationBase (the back-link + op.ObservationBase.Exists) and adds the git-specific measurement fields: `ObservedHEAD`, `ObservedRef`, `Bare`, `Dirty`, `Remotes`.

func NewObservation

func NewObservation(
	ofResource *Resource,
	exists bool,
	observedHEAD string,
	observedRef string,
	bare bool,
	dirty bool,
	remotes map[string]Remote,
) *Observation

NewObservation constructs a *Observation anchored to the resource it observes.

Parameters:

  • `ofResource`: the *Resource this observation is of. Must be non-nil (asserted by op.NewObservationBase).
  • `exists`: true when the path was a git repository at observation time.
  • `observedHEAD`: the disk's current HEAD SHA.
  • `observedRef`: the disk's current ref name.
  • `bare`: true when the on-disk repository is bare.
  • `dirty`: true when the working tree had uncommitted changes.
  • `remotes`: the on-disk remote configuration at observation time.

Returns:

  • `*Observation`: the constructed observation.

func (*Observation) String

func (o *Observation) String() string

String returns a debug-oriented single-line representation of the observation.

Returns:

  • `string`: `git.Observation{of=<OfResource.URI()>, exists=<bool>, head=<sha>, ref=<name>, bare=<bool>, dirty=<bool>, remotes=<count>}`.

type Provider

type Provider struct {
	op.ProviderBase
	// contains filtered or unexported fields
}

Provider provides git actions.

+devlore:access=planned

func NewProvider

func NewProvider(runtimeEnvironment *op.RuntimeEnvironment) *Provider

NewProvider constructs a Provider bound to `runtimeEnvironment`.

Parameters:

  • `runtimeEnvironment`: execution context.

Returns:

  • `*Provider`: the initialized provider.

func (*Provider) Checkout

func (p *Provider) Checkout(repo *Resource, ref string) (*Resource, error)

Checkout checks out a ref in the given repository directory.

`repo.Ref` and `repo.HEAD` are plan-time intent and are not mutated here. Callers that need the post-checkout disk state call Provider.Observe to obtain a *Observation carrying the disk's current `ObservedHEAD` / `ObservedRef`.

Parameters:

  • `repo`: git resource identifying the local repository.
  • `ref`: branch, tag, or commit to check out.

Returns:

  • `*Resource`: the repository resource (identity unchanged).
  • `error`: any error from `git checkout`.

func (*Provider) Clone

func (p *Provider) Clone(
	activationRecord *op.ActivationRecord,
	repository string,
	directory string,
	bare bool,
	branch string,
	depth int,
	filter string,
	noCheckout bool,
	noTags bool,
	origin string,
	recurseSubmodules bool,
	singleBranch bool,
	kwargs map[string]any,
) (*Resource, *Receipt, error)

Clone clones a repository into a newly created directory.

Identity for the cloned repository is constructed by NewResource from `directory`; operational metadata (Remotes, Bare, Dirty, HEAD) is populated by [Resource.Resolve] after the clone completes. When `directory` is empty, the directory name is derived from repository via [guessDirName] — the same algorithm git itself uses for `git clone <repository>` with no explicit directory.

The nine named options correspond one-to-one with `git clone` flags under the kwarg-to-flag rule (strip leading `--`, convert `-` to `_`, always expect a value — `--no-tags` becomes `no_tags=<bool>`). Any additional options a caller needs pass through kwargs and are translated using the same rule in reverse; see [buildCloneArgs].

Parameters:

  • `repository`: remote git URL (HTTPS, SSH, git protocol, or local path) to clone from.
  • `directory`: local filesystem path where the repository will be cloned; empty defers to git's own naming algorithm via [guessDirName].
  • `bare`: when true, emits `--bare` — bare repository (no working tree).
  • `branch`: when non-empty, emits `--branch <branch>` — branch, tag, or ref to check out.
  • `depth`: when > 0, emits `--depth <depth>` — shallow clone with truncated history.
  • `filter`: when non-empty, emits `--filter=<filter>` — partial-clone filter specification.
  • `noCheckout`: when true, emits `--no-checkout` — populate `.git/` but leave the working tree empty.
  • `noTags`: when true, emits `--no-tags` — do not fetch tags.
  • `origin`: when non-empty, emits `--origin <origin>` — name to use for the upstream remote in place of "origin".
  • `recurseSubmodules`: when true, emits `--recurse-submodules` — initialize and clone submodules recursively.
  • `singleBranch`: when true, emits `--single-branch` — fetch only the specified branch's history.
  • `kwargs`: catch-all for any `git clone` option not in the named set; each entry becomes an additional flag per the kwarg-to-flag rule.

Returns:

  • `*Resource`: the cloned git.Resource with populated metadata.
  • `*Resource`: the compensation handle — the same *Resource as the first return, passed to Provider.CompensateClone to reverse the clone. Git's Clone creates a directory rather than displacing one, so per the Tombstone rule (a tombstone exists for any object moved to a RecoverySite) there is no git tombstone; the compensation handle is the created Resource itself. Nil on error from `git clone` or resource construction; non-nil when the directory exists on disk even if [Resource.Resolve] failed afterward.
  • `error`: any error from directory derivation, resource construction, or the underlying `git clone`.

+devlore:defaults directory="",bare=false,branch="",depth=0,filter="",noCheckout=false,noTags=false,origin="",recurseSubmodules=false,singleBranch=false

func (*Provider) CompensateClone

func (p *Provider) CompensateClone(activationRecord *op.ActivationRecord, receipt *Receipt) error

CompensateClone removes the cloned directory.

Clone is a Bucket-B action: the cloned tree is creation, not displacement, so there is nothing to restore from op.RecoverySite — compensation just removes the directory. A nil receipt is a no-op (Clone never produced a resource to reverse).

Parameters:

  • `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
  • `receipt`: the *Receipt returned by Provider.Clone; may be nil.

Returns:

  • `error`: any error from os.RemoveAll on the cloned directory; nil when receipt or its resource is nil.

func (*Provider) Observe

func (p *Provider) Observe(repo *Resource) (*Observation, error)

Observe captures the runtime-observed state of a git repository on disk as a *Observation.

Reads `.git/` via the unexported [isGitRepo], [readHEADSha], [readBranchName], [readRemotes], and [isDirtyRepo] helpers. When the path is not a git repository, the Observation carries `Exists=false` and zero values for everything else — non-existence is a valid observation, not an error. Stat / read failures surface as errors.

Provider methods that previously called `r.Resolve()` and read fields off the Resource use `obs := p.Observe(r)` and read fields off the observation. `repo.HEAD` and `repo.Ref` on the Resource are plan-time intent; the disk's current state lives on the returned observation's `ObservedHEAD` and `ObservedRef`.

Parameters:

  • `repo`: the *Resource whose current git state to observe.

Returns:

  • `*Observation`: the constructed observation; never nil.
  • `error`: always nil — the `.git` reads are best-effort and non-existence is a valid observation; the error return keeps the announced fallible-action shape.

func (*Provider) Pull

func (p *Provider) Pull(repo *Resource) (*Resource, error)

Pull pulls the latest changes in the given repository directory.

`repo.Ref` and `repo.HEAD` are plan-time intent and are not mutated here. Callers that need the post-pull disk state call Provider.Observe.

Parameters:

  • `repo`: git resource identifying the local repository.

Returns:

  • `*Resource`: the repository resource (identity unchanged).
  • `error`: any error from `git pull`.

type Receipt

type Receipt struct {
	op.ReceiptBase
}

Receipt holds git-specific compensation state for a Provider.Clone call.

The embedded op.ReceiptBase carries the affected Resource (the cloned local repository) and the opaque op.ReceiptBase.TransactionID minted at op.ReceiptBase.Commit time. Clone is a Bucket-B (creation, not displacement) action — there is no prior content to archive, so the recovery key is the receipt's own transactionID; compensation simply removes the cloned directory tree.

Receipt has no provider-specific fields, so it inherits op.ReceiptBase.MarshalJSON and op.ReceiptBase.MarshalYAML unchanged. Only Receipt.RestoreEncoded is overridden, since rehydration requires the concrete Resource type that op.ReceiptBase cannot construct generically.

func NewReceipt

func NewReceipt(resource *Resource) *Receipt

NewReceipt constructs a Receipt anchored to the cloned Resource.

The transactionID and action name remain zero-valued until op.ReceiptBase.Commit is invoked when the receipt lands on a op.RecoveryStack via op.RecoveryStack.PushCompensator.

Parameters:

Returns:

  • `*Receipt`: the constructed receipt with only its resource populated.

func (*Receipt) RestoreEncoded

func (r *Receipt) RestoreEncoded(
	runtimeEnvironment *op.RuntimeEnvironment, base op.ReceiptData, _ map[string]any,
) error

RestoreEncoded reconstructs the receipt from its codec-decoded envelope, resolving its Resource against the rehydrated catalog.

It is the op.Receipt.RestoreEncoded override the recovery stack drives at re-arm (via [op.reconstructReceipt]) — the env is threaded in explicitly as a parameter, not read off the receiver, so the stack path (which loads a bare receipt before the catalog is rehydrated) can reconstruct it. The cloned Resource is resolved from `base.ResourceURI` via DiscoverResource; the base is re-seated via op.NewReceiptBase so op.ReceiptBase.Restore's URI-match check has a live resource, then Restore writes the full base. Receipt has no provider-specific fields, so `fields` is unused.

Parameters:

  • `runtimeEnvironment`: the resume environment; its catalog must hold (or be able to construct) the resource.
  • `base`: the codec-decoded base execution state.
  • `_`: the receipt's id-reference sub-field, unused (no provider-specific fields).

Returns:

  • `error`: non-nil only when the runtime environment or its catalog is missing; resolution and restore failures are verified-side defects and assert.

type Remote

type Remote struct {
	FetchURL string
	PushURL  string
}

Remote carries the fetch and push URLs for a named git remote.

PushURL is empty when the push direction uses FetchURL (git's default) — the distinction matters for workflows that split read and write endpoints (e.g., HTTPS fetch / SSH push, mirror fetch / authoritative push).

type Resource

type Resource struct {
	op.ResourceBase

	// SourcePath is the local clone's canonical absolute path; identity derives from this via the file:// URI. Not
	// persisted — reconstructed from the URI on deserialization.
	SourcePath fsroot.Path `json:"-" yaml:"-"`

	// Ref is the branch, tag, or commit reference the clone is positioned at as plan-time intent.
	// Set at construction by [Provider.Clone] (from the just-cloned tree's `.git/HEAD`) or by
	// serialized-form deserialization (from a saved plan). Not mutated by [Resource.Resolve]; the runtime
	// view of the disk's current ref lives on [Observation.ObservedRef].
	Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`

	// HEAD is the commit SHA (40-char hex) the clone was positioned at as plan-time intent. Set at
	// construction by [Provider.Clone] (from the just-cloned tree's `.git/HEAD`) or by serialized-form
	// deserialization. Pins the clone to an exact version across serialization. Empty for resources
	// constructed via [NewResource] without an associated clone. Not mutated by [Resource.Resolve];
	// the runtime view of the disk's current HEAD lives on [Observation.ObservedHEAD].
	HEAD string `json:"head,omitempty" yaml:"head,omitempty"`
}

Resource represents a cloned git repository.

Identity is the local clone's filesystem location, stored as a file:// URI in op.ResourceBase. Every domain field — Ref, HEAD, Remotes, Bare, Dirty — is populated by [Resource.Resolve] from the on-disk `.git/` contents. Ref and HEAD are additionally persisted through JSON/YAML so a serialized Resource can carry its version snapshot to contexts where Resolve cannot run (e.g., cross-host comparison, offline inspection); Remotes, Bare, and Dirty are operational and not persisted — they're always rebuilt by Resolve.

func DiscoverResource

func DiscoverResource(runtimeEnvironment *op.RuntimeEnvironment, value any) (*Resource, error)

DiscoverResource registers a git.Resource via op.ResourceCatalog.Discover without claiming production.

Use DiscoverResource from non-production callsites: receipt rehydration (UnmarshalJSON/Text/YAML), reference handles in CLI tools, and scanner-style discovery walks. The returned catalog entry has no producer stamp (or carries whatever stamp a previous NewResource call already applied). Use NewResource instead when the caller is a producer claiming this Resource as its output.

Discover does not stamp a producer, so unlike NewResource it takes only `runtimeEnvironment` — no unit reference is needed.

Nil-Catalog tolerance mirrors the receipt-rehydration paths: when `runtimeEnvironment.Catalog` is nil, the candidate is returned unlinked.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `value`: a string file path or file URI.

Returns:

  • `*Resource`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, or the input violates RFC 8089 when in file URI form.

func NewResource

func NewResource(runtimeEnvironment *op.RuntimeEnvironment, producerID string, value any) (*Resource, error)

NewResource constructs a git.Resource and claims production via op.ResourceCatalog.GetOrCreate.

Use NewResource from a producer dispatch context — typically a provider method that has received an op.ActivationRecord from the framework. The returned Resource is the canonical catalog entry, stamped with `producerID = unit.ID()` (or empty when `unit` is nil for non-graph dispatch). Use DiscoverResource instead when the caller is not claiming production (rehydration, reference handles, scanner-style discovery).

The input is a bare filesystem path ("/opt/repo"), or — on the catalog-rehydration round-trip — this provider's own emitted identity specific ("file://" + path), stripped back to the path. Identity is the canonical file:// specific computed from the resolved absolute path; remotes, ref, HEAD, and other metadata are populated post-construction by Clone, Resolve, or explicit setters.

Nil-Catalog tolerance mirrors DiscoverResource: when `runtimeEnvironment.Catalog` is nil (test fixtures, library callers without a runtime), the candidate is returned unlinked.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `producerID`: the producing caller's id (`activationRecord.CallerID`), or "" for caller-less dispatch. for non-graph dispatch.
  • `value`: a string file path or file URI.

Returns:

  • `*Resource`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, or the input violates RFC 8089 when in file URI form, or op.ResourceCatalog.GetOrCreate's strict assertions fail.

func (*Resource) Addressing

func (r *Resource) Addressing() op.AddressingMode

Addressing reports that git.Resource is location-keyed.

The identity is the local clone's filesystem location, and the bytes under that location (commit SHAs, working-tree contents) are mutable.

The catalog uses op.AddressingLocation semantics — content drift triggers shadow chains, not new URIs.

Returns:

func (*Resource) CanConvertFrom

func (*Resource) CanConvertFrom(source reflect.Type) bool

CanConvertFrom reports whether `source` can be projected into a *Resource via Resource.ConvertFrom.

Opts the git Resource into the framework's op.TargetConverter contract — accepted source shape is `string` (interpreted as a local clone's filesystem path or a git URL). The framework consults this probe both at plan-time via [op.typesAreInterconvertible] (the bubble-up parameter-consistency check honors the convertibility relation without running an actual conversion) and at dispatch-time via op.Convert step 7 (env-less fallback). The canonical dispatch-time path remains the registered constructor at op.Convert step 6, which receives the full op.RuntimeEnvironment and produces a fully-canonicalized Resource via [buildCandidate].

Cheap-probe contract: this method is called against a nil-or-zero `*Resource` receiver by [op.typesAreInterconvertible] during plan-time bubble-up checks. MUST NOT dereference receiver fields.

Parameters:

  • `source`: the candidate source type to test.

Returns:

  • `bool`: true when `source` is `string`.

func (*Resource) ConvertFrom

func (*Resource) ConvertFrom(value any) (any, error)

ConvertFrom projects `value` into an env-less unlinked *Resource.

Used by op.Convert step 7 when the env-aware registered constructor (step 6) is unavailable — env-less library callers, tests, or op.RuntimeEnvironment.Registry-missing contexts. The returned Resource carries only the SourcePath set from `value`; URI / Ref / HEAD / catalog interning are not populated here. Provider methods consuming the projected Resource are responsible for re-canonicalization via their own NewResource/DiscoverResource path when full identity is required.

Parameters:

  • `value`: the source value; must be `string`.

Returns:

  • `any`: the constructed unlinked *Resource.
  • `error`: non-nil when `value` is not a `string`.

func (*Resource) Digest

func (r *Resource) Digest() (op.Digest, error)

Digest returns the honest content hash for the local clone:

  • Clean repository (bare or working-tree): sha256 of HEAD's hex string.
  • Dirty working tree: sha256 of HEAD + "\n" + tree SHA over the index + working tree.

The HEAD SHA-1 itself already content-addresses git's commit graph; wrapping it in a sha256 layer keeps the algorithm consistent with the rest of the system (the catalog stores `op.Digest` values uniformly and round trips them through op.ParseDigest, which only accepts the sha256 allowlist). For dirty working trees, the tree SHA (derived from stash-create followed by rev-parse to the tree, not the commit SHA which would carry timestamps) captures the index + working-tree state deterministically — same state same digest.

Always fresh — recomputes at call time. Errors when the path is not a git repository or HEAD cannot be read.

Returns:

  • `op.Digest`: sha256 of the HEAD SHA (plus stash-create tree SHA when the working tree is dirty).
  • `error`: when the path is not a git repository or HEAD cannot be read.

func (*Resource) Equal

func (r *Resource) Equal(other any) bool

Equal reports whether r and other identify the same git resource.

Strict equality: other must be a *git.Resource (not merely an op.Resource with the same URI). Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal.

Parameters:

  • `other`: the value to compare against; may be any, including nil or a non-Resource.

Returns:

  • `bool`: true if `other` is a *git.Resource with the same URI as r.

func (*Resource) Etag

func (r *Resource) Etag() (string, error)

Etag returns a cheap stat-derived change-detection token for the local clone:

  • Bare repository: the 7-character HEAD short-id (e.g., "a1b2c3d").
  • Working tree, clean: the 7-character HEAD short-id.
  • Working tree, dirty: HEAD short-id + "-" + 7-character prefix of the tree SHA covering the current index + working tree.

The dirty fingerprint is derived from `git stash create` followed by `git rev-parse <stash>^{tree}`. The stash commit's own SHA cannot be used directly: commit objects include author/committer timestamps, so two calls on the same unchanged tree state would produce different commit SHAs (catalog would falsely detect drift on every Resolve). The tree SHA is content-addressed and timestamp-free — same tree state same SHA, different tree state different SHA. This lets the catalog detect drift within the dirty state without false-positive drift on identical state.

Always fresh — re-reads HEAD and (when dirty) re-runs the stash-create + rev-parse pair at call time. Errors when the path is not a git repository or HEAD cannot be read.

Returns:

  • `string`: the etag (HEAD short-id, optionally suffixed with `-<tree-short>` for a dirty working tree).
  • `error`: when the path is not a git repository or HEAD cannot be read.

func (*Resource) String

func (r *Resource) String() string

String returns a debug-oriented single-line representation of the resource.

Suitable for log lines and debug windows. Identity-only — runtime-observed state (bare, dirty, remotes, disk's current HEAD/ref) lives on *Observation, minted by Provider.Observe.

Returns:

  • `string`: `git.Resource{uri=<URI>, ref=<ref>, head=<head>}`.

func (*Resource) UnmarshalJSON

func (r *Resource) UnmarshalJSON(data []byte) error

UnmarshalJSON populates the receiver from its JSON document.

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method. Identity is reconstructed via NewResource from the URI; Ref and HEAD are assigned from the decoded snapshot. Operational state (Remotes, Bare, Dirty) stays at zero values until [Resource.Resolve] reads the on-disk clone.

Parameters:

  • `data`: JSON-encoded document.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the JSON does not decode, or resource construction fails.

func (*Resource) UnmarshalText

func (r *Resource) UnmarshalText(text []byte) error

UnmarshalText populates the receiver from raw UTF-8 bytes containing a local path or file URI.

Scalar form: only identity (URI) round-trips. Ref, HEAD, and Remotes remain at zero values; richer round trip uses Resource.UnmarshalJSON or Resource.UnmarshalYAML.

Parameters:

  • `text`: UTF-8 bytes containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing or resource construction fails.

func (*Resource) UnmarshalYAML

func (r *Resource) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML populates the receiver from its YAML document.

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method. Identity is reconstructed via NewResource from the URI; Ref and HEAD are assigned from the decoded snapshot. Operational state (Remotes, Bare, Dirty) stays at zero values until [Resource.Resolve] reads the on-disk clone.

Parameters:

  • `unmarshal`: callback supplied by the YAML decoder that projects the current node into the given target.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the YAML does not decode, or resource construction fails.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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