Skip to content
internal/repo
Edit
On this page

Package repo manages the .git/safegit/ data directory including initialization, configuration loading, validation, and path helpers for all state files.

#internal/repo

#internal/repo

Package repo manages the .git/safegit/ data directory including initialization, configuration loading, validation, and path helpers for all state files.

#Config

Go go
type Config struct

Config holds safegit configuration persisted in config.json.

A key this struct no longer declares (log.maxSizeMB, removed with oplog rotation) still LOADS from an existing config.json: encoding/json ignores unknown members. Writing one does not: GetConfigValue and SetConfigValue answer "unknown config key" for anything outside ValidConfigKeys.

#CommitConfig

Go go
type CommitConfig struct

CommitConfig holds commit-related settings.

#LockConfig

Go go
type LockConfig struct

LockConfig holds ref-lock acquisition settings.

#HooksConfig

Go go
type HooksConfig struct

HooksConfig holds hook-related settings.

#PrePrePushConfig

Go go
type PrePrePushConfig struct

PrePrePushConfig holds pre-pre-push hook timeout settings.

#PushConfig

Go go
type PushConfig struct

PushConfig holds push retry settings.

#UninstallTarget

Go go
type UninstallTarget struct

UninstallTarget is one path a repository-wide uninstall removes.

#DefaultConfig

Go go
func DefaultConfig() Config

DefaultConfig returns the default safegit configuration.

#SafegitDir

Go go
func SafegitDir(gitDir string) string

SafegitDir returns the path to .git/safegit/ given a .git directory path.

#SharedGitDir

Go go
func SharedGitDir(ctx context.Context, gitDir string) string

SharedGitDir returns the COMMON git directory: the one every worktree of a repository shares. For a normal repository it is gitDir itself; for a linked worktree, whose git dir is /worktrees/, it is .

It is the anchor for everything that is repository-level policy rather than checkout state -- the ref locks, and safegit's live hook store -- so that two worktrees can never disagree about it. Git's own hook directory is common too, which is why the pre-migration hook location is resolved from here.

The parameter accepts either the git directory (.git) or the safegit directory (.git/safegit); callers use both forms.

The answer never depends on the process working directory. CommonGitDirOf runs git in the git directory it is handed, so an answer that comes back relative is relative to THAT directory and is anchored there -- filepath.Abs, which would resolve it against this process's own directory, is exactly the wrong anchor and is not used.

#SharedSafegitDir

Go go
func SharedSafegitDir(ctx context.Context, gitDir string) string

SharedSafegitDir returns the safegit directory under the common .git dir. For normal repos this is identical to SafegitDir(gitDir). For worktrees it returns /safegit so that lock files and the live hook store are shared across all worktrees, ensuring proper serialization of ref updates and one repository-wide answer to which hooks run.

It takes the same parameter forms as SharedGitDir, whose resolution it is.

#IsInitialized

Go go
func IsInitialized(gitDir string) bool

IsInitialized reports whether this repository has a usable safegit data directory, which is decided by config.json rather than by the directory alone. A directory that exists without config.json is half-initialized -- an interrupted Init, or any stray subdirectory created under it -- and reporting that as initialized would make EnsureInitialized a no-op and leave every command failing on the missing config.json. Reporting it as uninitialized lets Init complete it (Init is idempotent over the directories it creates).

#Init

Go go
func Init(ctx context.Context, gitDir string) error

Init creates the .git/safegit/ directory structure and writes default config.json. Idempotent: returns nil if already initialized.

The context is the dispatch's own: the worktree check below asks git where the common git directory is, and that call belongs on the same context as every other git call the invocation makes.

#EnsureInitialized

Go go
func EnsureInitialized(ctx context.Context, gitDir string) error

EnsureInitialized auto-initializes .git/safegit/ if it doesn't exist yet.

#UninstallPlan

Go go
func UninstallPlan(ctx context.Context, gitDir string) ([]UninstallTarget, error)

UninstallPlan enumerates every path a repository-wide uninstall removes, without removing any of them. It returns an error when there is nothing to remove, which is the "safegit is not initialized" refusal.

Enumerating and removing are deliberately separate: the plan is what the command prints before it asks for consent, and the removal itself goes through the caller's effects handle so that a dry run records it instead of performing it. There is no companion function that both plans and removes -- one existed, only its own tests called it, and it could not be previewed.

What the plan covers: safegit's state directory entirely -- which since the hook store moved there takes the installed hooks with it -- plus the repository-level state under the common git dir in worktree setups (the shared locks and the live hook store, which is the one pushes actually run), plus the two safegit-owned names that may still be sitting in git's own hook directory from before the move. Leaving any of those behind would keep an uninstalled tool's checks running on every push with no state directory left to explain where they came from.

The hook store the CHECKOUT provides (.safegit/hooks in the work tree) is deliberately absent from the plan: it is part of the repository's content, shared with everyone who cloned it, and removing it would be an uncommitted deletion of somebody else's file.

Uninstalling is a REPOSITORY operation, not a per-checkout one. A repository with linked worktrees holds safegit state in one directory per worktree git dir, plus the shared store under the common git dir where the locks and the hooks every push runs live. An uninstall that took only the invoking worktree's directory left the rest of it in place while reporting the tool uninstalled -- config, oplog and all.

The set of state directories is read off disk rather than from git worktree list. A worktree's git dir is always /worktrees/, so the listing is exact, and it still finds the state of a worktree whose checkout has been deleted but not yet pruned -- one git reports as prunable, and whose state an uninstall driven by that list would leave behind.

#LoadConfig

Go go
func LoadConfig(gitDir string) (*Config, error)

LoadConfig reads and parses config.json from the safegit directory.

#LoadConfigFrom

Go go
func LoadConfigFrom(path string) (*Config, error)

LoadConfigFrom reads and parses config from an arbitrary path.

#MarshalConfig

Go go
func MarshalConfig(cfg *Config) ([]byte, error)

MarshalConfig renders config.json's exact bytes. Rendering is split from writing so callers mint the write as an effect instead of performing it here, which is what lets --dry-run record a config change without making one.

This package therefore writes config.json in exactly one place -- Init, whose write goes through writeFileAtomic. config set renders here and hands the bytes to the effects handle. A save helper that plain-writes the file would be a third, non-atomic writer of the path every command reads.

#ConfigPath

Go go
func ConfigPath(gitDir string) string

ConfigPath is where config.json lives for the given git dir.

#GetConfigValue

Go go
func GetConfigValue(cfg *Config, key string) (interface{}, error)

GetConfigValue returns the value for a dot-separated config key.

#SetConfigValue

Go go
func SetConfigValue(cfg *Config, key, value string) error

SetConfigValue sets a dot-separated config key to the given string value.

The key is resolved BEFORE the value is parsed, so an unknown key is always reported as an unknown key whatever its value looks like: config set log.maxSizeMB abc names the retired key, not the shape of "abc".

#ValidConfigKeys

Go go
func ValidConfigKeys() []string

ValidConfigKeys returns the list of supported config keys.

#Config.Validate

Go go
func (c *Config) Validate() error

Validate checks that all config values are within acceptable ranges.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • rlsbl Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search