Artifact e2831626a7c310ea38d0795900b02fe88f0698fcbe2dd0e433095640508dbecb:
- File
cli/shared.go
— part of check-in
[f5744359d3]
at
2026-04-19 16:30:54
on branch trunk
— Initial open source release
libfossil is a pure-Go implementation of the Fossil SCM, reading and writing the same .fossil SQLite repository format as upstream Fossil.
This is the first public release of code that previously lived in a private repository. The module path is github.com/danmestas/libfossil and the binary is named libfossil to avoid $PATH collisions with upstream fossil.
Included: - Repo lifecycle (create, clone), checkout/checkin, timeline, merge, diff, annotate, shun, tag, branch, bisect, stash, undo/redo - Sync-over-HTTP protocol (client and handler) - Two SQLite drivers: modernc (pure Go, default) and ncruces (wasm-capable) - Observer interfaces (SyncObserver, CheckoutObserver) with an optional OpenTelemetry implementation in observer/otel (separate submodule) - Deterministic simulation testing harness with BUGGIFY fault injection - Kong-based embeddable CLI under cli/ and a standalone binary under cmd/libfossil/ (user: [email protected], size: 4232) [annotate] [blame] [check-ins using]
package cli import ( "database/sql" "fmt" "os" "os/user" "path/filepath" libfossil "github.com/danmestas/libfossil" "github.com/danmestas/libfossil/internal/repo" ) // Globals holds flags shared by all CLI commands. type Globals struct { Repo string `short:"R" help:"Path to repository file" type:"path"` Verbose bool `short:"v" help:"Verbose output"` } // OpenRepo opens a Fossil repository using the handle API. // If Repo is empty, it searches for a .fossil file or .fslckout checkout. func (g *Globals) OpenRepo() (*libfossil.Repo, error) { if g.Repo == "" { found, err := findRepo() if err != nil { return nil, fmt.Errorf("no repository specified (use -R <path>)") } g.Repo = found } return libfossil.Open(g.Repo) } // openInternalRepo opens the internal repo handle for commands that need // raw DB access (query, info, wiki, tag list, etc.). func openInternalRepo(g *Globals) (*repo.Repo, error) { r, err := g.OpenRepo() if err != nil { return nil, err } // The caller is responsible for closing this via r.Close() on the // public handle; we hand back the inner pointer for DB queries. return r.Inner(), nil } // resolveRID resolves a version string to a rid. // Accepts: empty/"tip" (most recent checkin), "trunk" (tagged trunk tip), // UUID prefix (min 4 chars), or full UUID. func resolveRID(r *libfossil.Repo, version string) (int64, error) { db := r.Inner().DB() switch version { case "", "tip": var rid int64 err := db.QueryRow( "SELECT objid FROM event WHERE type='ci' ORDER BY mtime DESC LIMIT 1", ).Scan(&rid) if err != nil { return 0, fmt.Errorf("no checkins found") } return rid, nil case "trunk": var rid int64 err := db.QueryRow(` SELECT tagxref.rid FROM tagxref JOIN tag ON tag.tagid=tagxref.tagid WHERE tag.tagname='sym-trunk' AND tagxref.tagtype>0 ORDER BY tagxref.mtime DESC LIMIT 1`, ).Scan(&rid) if err != nil { return resolveRID(r, "tip") } return rid, nil default: var rid int64 err := db.QueryRow( "SELECT rid FROM blob WHERE uuid LIKE ?", version+"%", ).Scan(&rid) if err != nil { return 0, fmt.Errorf("artifact %q not found", version) } return rid, nil } } // currentUser returns the current OS username, or "anonymous" if unavailable. func currentUser() string { if u, err := user.Current(); err == nil { return u.Username } return "anonymous" } // findRepo searches the current directory and its parents for a .fossil file // or a .fslckout checkout database that points to a repo. func findRepo() (string, error) { dir, err := os.Getwd() if err != nil { return "", err } for { ckout := filepath.Join(dir, ".fslckout") if _, err := os.Stat(ckout); err == nil { return repoFromCheckout(ckout) } matches, _ := filepath.Glob(filepath.Join(dir, "*.fossil")) if len(matches) == 1 { return matches[0], nil } parent := filepath.Dir(dir) if parent == dir { break } dir = parent } return "", fmt.Errorf("no .fossil file found") } // repoFromCheckout reads the repository path from a .fslckout database. func repoFromCheckout(ckoutPath string) (string, error) { db, err := sql.Open("sqlite", ckoutPath+"?mode=ro") if err != nil { return "", err } defer db.Close() var repoPath string err = db.QueryRow("SELECT value FROM vvar WHERE name='repository'").Scan(&repoPath) if err != nil { return "", fmt.Errorf("checkout %s: no repository path found", ckoutPath) } return repoPath, nil } // openCheckout opens the .fslckout database in the given directory. func openCheckout(dir string) (*sql.DB, error) { ckoutPath := filepath.Join(dir, ".fslckout") if _, err := os.Stat(ckoutPath); err != nil { ckoutPath = filepath.Join(dir, "_FOSSIL_") if _, err := os.Stat(ckoutPath); err != nil { return nil, fmt.Errorf("no checkout found in %s (run 'fossil repo open' first)", dir) } } return sql.Open("sqlite", ckoutPath) } // checkoutVid returns the current checkout version ID from vvar. func checkoutVid(db *sql.DB) (int64, error) { var vid int64 err := db.QueryRow("SELECT value FROM vvar WHERE name='checkout'").Scan(&vid) if err != nil { return 0, fmt.Errorf("reading checkout version: %w", err) } return vid, nil }