perf(git): cache git rev-parse results within sessions
Some checks failed
CI / Check for .beads changes (push) Has been skipped
CI / Check embedded formulas (push) Failing after 20s
CI / Test (push) Failing after 1m28s
CI / Lint (push) Failing after 21s
CI / Integration Tests (push) Successful in 1m12s
CI / Coverage Report (push) Has been skipped
Windows CI / Windows Build and Unit Tests (push) Has been cancelled
Some checks failed
CI / Check for .beads changes (push) Has been skipped
CI / Check embedded formulas (push) Failing after 20s
CI / Test (push) Failing after 1m28s
CI / Lint (push) Failing after 21s
CI / Integration Tests (push) Successful in 1m12s
CI / Coverage Report (push) Has been skipped
Windows CI / Windows Build and Unit Tests (push) Has been cancelled
Multiple gt commands call git rev-parse --show-toplevel, adding ~50ms each invocation. Results rarely change within a session, and multiple agents calling git concurrently contend on .git/index.lock. Add cached RepoRoot() and RepoRootFrom() functions to the git package and update all callers to use them. This ensures a single git subprocess call per process for the common case of checking the current directory's repo root. Files updated: - internal/git/git.go: Add RepoRoot() and RepoRootFrom() - internal/cmd/prime.go: Use cached git.RepoRoot() - internal/cmd/molecule_status.go: Use cached git.RepoRoot() - internal/cmd/sling_helpers.go: Use cached git.RepoRoot() - internal/cmd/rig_quick_add.go: Use git.RepoRootFrom() for path arg - internal/version/stale.go: Use cached git.RepoRoot() Closes: bd-2zd.5
This commit is contained in:
@@ -4,13 +4,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/config"
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/style"
|
"github.com/steveyegge/gastown/internal/style"
|
||||||
"github.com/steveyegge/gastown/internal/workspace"
|
"github.com/steveyegge/gastown/internal/workspace"
|
||||||
)
|
)
|
||||||
@@ -873,13 +873,9 @@ func outputMoleculeCurrent(info MoleculeCurrentInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getGitRootForMolStatus returns the git root for hook file lookup.
|
// getGitRootForMolStatus returns the git root for hook file lookup.
|
||||||
|
// Uses cached value to avoid repeated git subprocess calls.
|
||||||
func getGitRootForMolStatus() (string, error) {
|
func getGitRootForMolStatus() (string, error) {
|
||||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
return git.RepoRoot()
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(out)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTownLevelRole returns true if the agent ID is a town-level role.
|
// isTownLevelRole returns true if the agent ID is a town-level role.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/lock"
|
"github.com/steveyegge/gastown/internal/lock"
|
||||||
"github.com/steveyegge/gastown/internal/state"
|
"github.com/steveyegge/gastown/internal/state"
|
||||||
"github.com/steveyegge/gastown/internal/style"
|
"github.com/steveyegge/gastown/internal/style"
|
||||||
@@ -527,13 +528,9 @@ func buildRoleAnnouncement(ctx RoleContext) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getGitRoot returns the root of the current git repository.
|
// getGitRoot returns the root of the current git repository.
|
||||||
|
// Uses cached value to avoid repeated git subprocess calls.
|
||||||
func getGitRoot() (string, error) {
|
func getGitRoot() (string, error) {
|
||||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
return git.RepoRoot()
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(out)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAgentIdentity returns the agent identity string for hook lookup.
|
// getAgentIdentity returns the agent identity string for hook lookup.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/style"
|
"github.com/steveyegge/gastown/internal/style"
|
||||||
"github.com/steveyegge/gastown/internal/workspace"
|
"github.com/steveyegge/gastown/internal/workspace"
|
||||||
)
|
)
|
||||||
@@ -138,13 +139,7 @@ func runRigQuickAdd(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findGitRoot(path string) (string, error) {
|
func findGitRoot(path string) (string, error) {
|
||||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
return git.RepoRootFrom(path)
|
||||||
cmd.Dir = path
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(out)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findGitRemoteURL(gitRoot string) (string, error) {
|
func findGitRemoteURL(gitRoot string) (string, error) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/constants"
|
"github.com/steveyegge/gastown/internal/constants"
|
||||||
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/tmux"
|
"github.com/steveyegge/gastown/internal/tmux"
|
||||||
"github.com/steveyegge/gastown/internal/workspace"
|
"github.com/steveyegge/gastown/internal/workspace"
|
||||||
)
|
)
|
||||||
@@ -331,13 +332,13 @@ func ensureAgentReady(sessionName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// detectCloneRoot finds the root of the current git clone.
|
// detectCloneRoot finds the root of the current git clone.
|
||||||
|
// Uses cached value to avoid repeated git subprocess calls.
|
||||||
func detectCloneRoot() (string, error) {
|
func detectCloneRoot() (string, error) {
|
||||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
root, err := git.RepoRoot()
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("not in a git repository")
|
return "", fmt.Errorf("not in a git repository")
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(out)), nil
|
return root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// detectActor returns the current agent's actor string for event logging.
|
// detectActor returns the current agent's actor string for event logging.
|
||||||
|
|||||||
@@ -9,8 +9,49 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Cached repo root for the current process.
|
||||||
|
// Since CLI commands are short-lived and the working directory doesn't change
|
||||||
|
// during a single invocation, caching this avoids repeated git subprocess calls
|
||||||
|
// that add ~50ms each and contend on .git/index.lock.
|
||||||
|
var (
|
||||||
|
cachedRepoRoot string
|
||||||
|
cachedRepoRootOnce sync.Once
|
||||||
|
cachedRepoRootErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
// RepoRoot returns the root directory of the git repository containing the
|
||||||
|
// current working directory. The result is cached for the lifetime of the process.
|
||||||
|
// This avoids repeated git rev-parse calls that are expensive (~50ms each) and
|
||||||
|
// can cause lock contention when multiple agents are running.
|
||||||
|
func RepoRoot() (string, error) {
|
||||||
|
cachedRepoRootOnce.Do(func() {
|
||||||
|
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
cachedRepoRootErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cachedRepoRoot = strings.TrimSpace(string(out))
|
||||||
|
})
|
||||||
|
return cachedRepoRoot, cachedRepoRootErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoRootFrom returns the root directory of the git repository containing the
|
||||||
|
// specified path. Unlike RepoRoot(), this is not cached because it depends on
|
||||||
|
// the input path. Use RepoRoot() when checking the current working directory.
|
||||||
|
func RepoRootFrom(path string) (string, error) {
|
||||||
|
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
||||||
|
cmd.Dir = path
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(out)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// GitError contains raw output from a git command for agent observation.
|
// GitError contains raw output from a git command for agent observation.
|
||||||
// ZFC: Callers observe the raw output and decide what to do.
|
// ZFC: Callers observe the raw output and decide what to do.
|
||||||
// The error interface methods provide human-readable messages, but agents
|
// The error interface methods provide human-readable messages, but agents
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These variables are set at build time via ldflags in cmd package.
|
// These variables are set at build time via ldflags in cmd package.
|
||||||
@@ -133,9 +135,8 @@ func GetRepoRoot() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if current directory is in a gastown repo
|
// Check if current directory is in a gastown repo
|
||||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
// Uses cached git.RepoRoot() to avoid repeated subprocess calls
|
||||||
if output, err := cmd.Output(); err == nil {
|
if root, err := git.RepoRoot(); err == nil {
|
||||||
root := strings.TrimSpace(string(output))
|
|
||||||
if hasGastownMarker(root) {
|
if hasGastownMarker(root) {
|
||||||
return root, nil
|
return root, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user