git

package
v1.801.477 Latest Latest
Warning

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

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

Documentation

Overview

Package git is Git hosting for your org: create repos, clone, push, and see what they cost.

It mounts the Hanzo Cloud /v1/git surface: S3-backed Git hosting native in the unified cloud binary — Hanzo Git, the internal git host foundation agents push code into.

A repo is the Git LAYER (source code, buildable/deployable) that lives UNDER an IAM project. It is NOT the IAM project itself: `project` is org-scoping CONTEXT (org → project → env); a repo is scoped BY that context. Every repo belongs to exactly one org (the gateway-minted X-Org-Id, HIP-0026) and an optional project sub-scope (X-Project-Id), enforced on every query, so one org can never read, clone, push to, or delete another's repos.

Surface:

POST   /v1/git/repos            create a bare repo            -> repoView (201)
GET    /v1/git/repos            list the org's repos       -> {data:[repoView]}
GET    /v1/git/repos/:name      repo detail (branches, HEAD)  -> repoView
DELETE /v1/git/repos/:name      delete + purge storage        -> 204
GET    /v1/git/usage            per-repo + total bytes        -> usageView

Smart-HTTP git protocol (so `git clone` / `git push` work natively):

GET  /v1/git/:org/:repo/info/refs?service=git-upload-pack|git-receive-pack
POST /v1/git/:org/:repo/git-upload-pack     (clone/fetch)
POST /v1/git/:org/:repo/git-receive-pack    (push)

A project-scoped repo names its project as a middle segment (/v1/git/:org/:project/:repo/…, git@host:org/project/repo.git). The scope otherwise rides X-Project-Id, and a git client sends no headers, so the path is the only channel that reaches a remote. Two names are only unique within one project — hanzo/hanzo-apps/ai and hanzo/hanzo-docs/ai are distinct repos.

Storage is bare git repos on a real filesystem (osfs) rooted under {DataDir}/git; go-git initializes + reads them, while the heavy clone/push/ mirror paths stream through the `git` CLI (gitexec.go) so multi-GB packs stay bounded in memory. See storage.go for the hanzoai/vfs (S3) storage seam.

Billing: every repo tracks sizeBytes, re-measured on create and after each push. /v1/git/usage exposes per-repo + total bytes per org, and each measurement emits a "git.usage" log line a metering consumer can bill on.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoRepository — the repository does not exist, or has no object store yet.
	ErrNoRepository = errors.New("git: no such repository")
	// ErrNoRevision — the ref or revision does not resolve. An empty repository
	// with no commits answers this too.
	ErrNoRevision = errors.New("git: no such revision")
	// ErrNoPath — the path does not exist at that revision.
	ErrNoPath = errors.New("git: no such path")
	// StopWalk ends a WalkText early without being an error.
	StopWalk = errors.New("git: stop walk")
)

Errors a Repository returns, so handlers can map to HTTP without knowing which backend produced them. Before this, browse.go turned ANY error from resolveRef into 404 "unknown ref", which quietly reported a broken repository as a missing branch.

Functions

func CloneURL

func CloneURL(org, name string) string

CloneURL returns the HTTPS smart-HTTP clone URL for an org's repo (https://<domain>/v1/git/<org>/<repo>.git) — the exact URL the git handlers serve. Empty when git is not mounted. The credential is NEVER embedded here; the sandbox presents it out of band (env-fed http.extraHeader), so this URL is safe to log and to hand to a subprocess on argv.

func IndexRepoActivity

func IndexRepoActivity(ctx context.Context, in indexInput) error

IndexRepoActivity is the durable index step: read the repo tip's tree from the object plane and fold its text files into the org's code index. Idempotent (full-tree reconcile with prune), so a retry or redelivery re-converges. Exported for worker registration; not called directly.

func IndexRepoWorkflow

func IndexRepoWorkflow(ctx workflow.Context, in indexInput) error

IndexRepoWorkflow indexes one repo's pushed tip as a single durable activity, with retry/backoff. Exported for worker registration; not called directly.

func MatchPaths

func MatchPaths(ctx context.Context, repo Repository, rev Revision, glob string) ([]string, error)

MatchPaths lists every file path at rev selected by glob, sorted.

The glob is matched SEGMENT BY SEGMENT with path.Match, so `*` never crosses a `/` — `values/*/*.yaml` selects `values/hanzo/www.yaml` and not `values/a/b/c.yaml`. `**` matches zero or more whole segments; as the final segment it selects every file beneath. Only files are returned: a directory is something to descend, never a result.

Matching is prefix-pruned — a segment that matches nothing stops that branch — so a specific glob reads a handful of trees rather than the whole commit.

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires the git surface onto app per HIP-0106.

func NotifyPostActivity

func NotifyPostActivity(ctx context.Context, in notifyPost) error

NotifyPostActivity renders and posts one message to one channel. Exported for worker registration; not called directly.

func NotifyTargetsActivity

func NotifyTargetsActivity(ctx context.Context, ev cloud.LifecycleEvent) ([]string, error)

NotifyTargetsActivity resolves the channels subscribed to the event's repo that opted into its kind. It is an activity because it reads a store — a workflow must stay deterministic on replay, so the resolved list is recorded in history instead. Exported for worker registration; not called directly.

func NotifyWorkflow

func NotifyWorkflow(ctx workflow.Context, ev cloud.LifecycleEvent) error

NotifyWorkflow delivers one lifecycle fact to every subscribed channel: one activity resolves the targets, then ONE activity per channel.

Per-channel activities are what make retry safe. Durable execution replays a completed activity from history instead of re-running it, so a retry never re-posts to a channel that already received the message — the duplicate-delivery hazard of retrying one activity that loops over every channel.

A channel whose retries are exhausted does not withhold the others: the pre-durable behaviour was that a failed post is logged and never fatal, and that is preserved. Exported for worker registration; not called directly.

func SetIndexer

func SetIndexer(fn Indexer)

SetIndexer injects the code-index reactor. The composition root calls it once, after the git and code subsystems both mount. Nil leaves push-index inert.

func ShortRev

func ShortRev(r Revision) string

ShortRev is the abbreviated form used for display. It is a projection, never an identifier: pass the full Revision back to the Repository. Seven characters, matching the shortSha the browse API has always emitted.

func Shutdown

func Shutdown() error

Shutdown stops the SSH listener and closes every open store (per-org repo metadata + the SSH key registry). Idempotent.

func VerifyRef

func VerifyRef(ctx context.Context, org, repo, branch string) (sha string, ok bool)

VerifyRef reports the tip commit of branch in an org's repo, reading the on-disk bare repo directly (the shared git storage every cloud replica mounts). It is the independent, in-process confirmation that a branch a sandbox claims to have pushed actually LANDED in native git — cloud trusts the branch tips it can read, not the remote runner's self-report. ok is false when git is unmounted, the repo/branch is absent, or the read fails (fail-closed: an unverifiable ref is treated as absent).

Types

type Blob

type Blob struct {
	Path      string
	Size      int64
	Content   []byte
	Binary    bool
	Truncated bool
}

Blob is the content of one path at one revision. Binary reports that the bytes are not valid text; Truncated reports that the file exceeded the caller's limit, in which case Content is empty — a large file is offered as a clone, never as megabytes of JSON.

type Change

type Change struct {
	Rev         Revision
	Message     string
	AuthorName  string
	AuthorEmail string
	When        time.Time
}

Change is one entry of a revision's history.

type Entry

type Entry struct {
	Name string
	Path string
	Dir  bool
	Size int64
	Mode string
}

Entry is one immediate child of a directory in a revision's tree. Dir distinguishes a subtree from a file; Mode is the backend's own permission string (octal for git), carried through for display only.

type IndexedFile

type IndexedFile struct {
	Path    string
	Content string
}

IndexedFile is one text file handed across the seam to the code index: its repo-relative path and content.

type Indexer

type Indexer func(ctx context.Context, org, billingOrg, project, repo string, files []IndexedFile) error

Indexer folds a pushed repo's text files into the code-intelligence index. Injected at the composition root so the git plane stays free of a clients/code import (the two planes never import each other).

type MirrorTarget

type MirrorTarget struct {
	ID        string
	Org       string
	Project   string
	Repo      string
	Host      string
	URL       string
	CreatedAt int64
}

MirrorTarget is a downstream remote a repo's advanced refs are mirrored to (GitHub/GitLab/self). Keyed by (org, repo, host): one target per host per repo.

type Ref

type Ref struct {
	Name string
	Rev  Revision
}

Ref is a human-facing name — a branch or a tag — bound to the revision it currently points at.

type Repo

type Repo struct {
	ID            string
	Org           string
	Project       string // may be "" (org-level repo)
	Name          string
	Description   string
	DefaultBranch string
	Public        bool // public repos allow ANONYMOUS read (upload-pack); writes stay org-authed
	SizeBytes     int64
	CreatedAt     int64
	UpdatedAt     int64
}

Repo is the org-scoped, canonical metadata record for one Git repository. Org isolation is the (org, project) pair, enforced at the query layer; the gateway-minted X-Org-Id (HIP-0026) selects the org and X-Project-Id an optional sub-scope. The repo's OBJECTS (packs, refs) live on the billy-backed storage under the same (org, project, name) path — this row is only the metadata + the last-measured storage size that commerce meters on.

type Repository

type Repository interface {
	// Refs lists branches and tags, each sorted by name.
	Refs(ctx context.Context) (branches, tags []Ref, err error)

	// Resolve turns a ref name into a revision. An empty name means the
	// repository's own default (HEAD, else the configured default branch). It
	// returns the revision plus the label that was actually used, so a caller
	// can echo "which branch am I looking at" without re-deriving it.
	Resolve(ctx context.Context, ref string) (Revision, string, error)

	// Tree lists the immediate children of dir at rev, directories first then
	// files, each group sorted by name. The root is "".
	Tree(ctx context.Context, rev Revision, dir string) ([]Entry, error)

	// Blob reads one path at rev. A file larger than maxBytes comes back with
	// Truncated set and no content; maxBytes <= 0 means no limit.
	Blob(ctx context.Context, rev Revision, path string, maxBytes int64) (Blob, error)

	// Log walks history from rev, newest first, at most limit entries. A
	// non-empty path restricts the walk to changes touching that path.
	Log(ctx context.Context, rev Revision, path string, limit int) ([]Change, error)

	// DefaultBranch reports the branch the repository points HEAD at.
	DefaultBranch(ctx context.Context) (string, error)

	// WalkText visits every TEXT file in rev's tree. Binary files are skipped —
	// the one consumer is code intelligence, which indexes source, not blobs.
	// A file larger than maxFileBytes is skipped WITHOUT being read, so a huge
	// blob never lands in memory (maxFileBytes <= 0 means no limit); this is why
	// the cap belongs here and not in the callback. Returning an error from fn
	// stops the walk and surfaces that error; returning StopWalk stops it cleanly.
	WalkText(ctx context.Context, rev Revision, maxFileBytes int64, fn func(path, content string) error) error
}

The read model. Every method is safe to call on an empty repository: an unborn tree answers ErrNoRevision rather than panicking, which is what lets Refs report an empty-but-valid repo instead of a 500.

type Revision

type Revision string

Revision identifies one immutable state of a Repository. It is OPAQUE: consumers pass it back to the Repository that produced it and never parse it. The git backend fills it with a commit sha; that is an implementation detail and the reason this is a distinct type rather than a bare string.

func (Revision) String

func (r Revision) String() string

String renders a revision for display and for JSON. Callers that want a short form use ShortRev, which is display-only and must never be fed back as input.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is one org's repo-metadata database — ONE SQLite file per org at {DataDir}/orgs/{orgSlug}/git.db (opened via cloud.OrgDB). git is org-scoped, not project-scoped: /v1/git/usage is a deliberate org-wide rollup across every project, so the physical boundary is the org and the (optional) project is a row column. MaxOpenConns(1) serializes writes against the file lock.

func (*Store) ClearConflict

func (s *Store) ClearConflict(ctx context.Context, org, project, repo, branch string) error

ClearConflict removes one branch's divergence marker — a later ff-apply reconciled it. Idempotent (no row ⇒ no-op).

func (*Store) ClearRepoConflicts

func (s *Store) ClearRepoConflicts(ctx context.Context, org, project, repo string) error

ClearRepoConflicts removes every branch's divergence marker for a repo — a full re-import force-fetches every ref, reconciling the repo wholesale. Idempotent.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) ConflictRepoSet

func (s *Store) ConflictRepoSet(ctx context.Context, org, project string) (map[string]bool, error)

ConflictRepoSet returns the set of repos (by name) with ≥1 unresolved inbound conflict in (org, project) — one query backing the repo-list status roll-up.

func (*Store) Create

func (s *Store) Create(ctx context.Context, r Repo) error

Create inserts a new repo row. Returns errConflict when (org,project,name) already exists in the org.

func (*Store) CreateMirror

func (s *Store) CreateMirror(ctx context.Context, v MirrorTarget) error

CreateMirror inserts a mirror target. errConflict when (org,repo,host) exists.

func (*Store) CreateSubscription

func (s *Store) CreateSubscription(ctx context.Context, v Subscription) error

CreateSubscription inserts a subscription. errConflict when (org,repo,channel) already exists — one repo can subscribe a given channel exactly once.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, org, project, name string) (bool, error)

Delete removes a repo row AND cascade-deletes its lifecycle config (subscriptions + mirror targets) in one transaction, so a deleted repo can never leave an orphaned external mirror target that a re-created repo of the same name would silently inherit (Red MED-3: exfil-on-recreate). Reports whether the repo row went.

func (*Store) DeleteMirror

func (s *Store) DeleteMirror(ctx context.Context, org, project, repo, id string) (bool, error)

DeleteMirror removes a mirror target by (org, project, repo, id). Reports whether a row went.

func (*Store) DeleteSubscription

func (s *Store) DeleteSubscription(ctx context.Context, org, project, repo, id string) (bool, error)

DeleteSubscription removes a subscription by (org, project, repo, id) — a caller may only delete their own org's subscription of the named repo IN SCOPE. Reports whether a row went.

func (*Store) Get

func (s *Store) Get(ctx context.Context, org, project, name string) (Repo, error)

Get returns the repo for (org,project,name) or errNotFound.

func (*Store) List

func (s *Store) List(ctx context.Context, org, project string) ([]Repo, error)

List returns every repo for (org,project), most-recently-updated first.

func (*Store) ListMirrors

func (s *Store) ListMirrors(ctx context.Context, org, project, repo string) ([]MirrorTarget, error)

ListMirrors returns every mirror target for the repo (org, project, repo), newest first.

func (*Store) ListOrg

func (s *Store) ListOrg(ctx context.Context, org string) ([]Repo, error)

ListOrg returns every repo across ALL projects for org (usage rollup), most-recently-updated first.

func (*Store) ListPublic

func (s *Store) ListPublic(ctx context.Context, org string) ([]Repo, error)

ListPublic returns every PUBLIC repo across all projects for org, newest first — the per-org half of the anonymous explore/discovery surface.

func (*Store) ListSubscriptions

func (s *Store) ListSubscriptions(ctx context.Context, org, project, repo string) ([]Subscription, error)

ListSubscriptions returns every subscription for the repo (org, project, repo), newest first.

func (*Store) RecordConflict

func (s *Store) RecordConflict(ctx context.Context, org, project, repo, branch, detail string, at int64) error

RecordConflict upserts the divergence marker for one branch: the upstream push could not fast-forward native, so native was preserved and this row records the split-brain for the console + operator. Upsert (not insert) so a repeated diverging push refreshes the detail/timestamp instead of erroring.

func (*Store) SetPublic

func (s *Store) SetPublic(ctx context.Context, org, project, name string, public bool, updatedAt int64) error

SetPublic flips a repo's visibility and bumps updated_at. Public grants ANONYMOUS READ (upload-pack) only — receive-pack stays org-authed always.

func (*Store) SetSize

func (s *Store) SetSize(ctx context.Context, org, project, name string, sizeBytes, updatedAt int64) error

SetSize records the last-measured storage size for a repo and bumps updated_at. Called on create and after each push, so the metered number is always the real on-disk size, never a fabricated rollup.

type Subscription

type Subscription struct {
	ID        string
	Org       string
	Project   string
	Repo      string
	Channel   string
	Events    string
	CreatedAt int64
}

Subscription binds a repo (by org+name) to a Slack channel for lifecycle notifications. Events is a CSV of LifecycleKind wire names; "" means every supported kind. Project is the scope it was created in (display only) — routing keys on (org, repo).

Jump to

Keyboard shortcuts

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