google-labs-jules[bot]
Final deployment for HF with landing page
93d826e
package db
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/fatih/color"
)
const (
maxGitRetries = 5
baseGitRetryDelay = 100 * time.Millisecond
)
func init() {
// ensure git is available
cmd := exec.Command("git", "--version")
if err := cmd.Run(); err != nil {
panic(fmt.Errorf("error running git --version: %v", err))
}
}
type GitRepo struct {
orgId string
planId string
}
func InitGitRepo(orgId, planId string) error {
dir := getPlanDir(orgId, planId)
return initGitRepo(dir)
}
func initGitRepo(dir string) error {
// Set the default branch name to 'main' for the new repository
res, err := exec.Command("git", "-C", dir, "init", "-b", "main").CombinedOutput()
if err != nil {
return fmt.Errorf("error initializing git repository with 'main' as default branch for dir: %s, err: %v, output: %s", dir, err, string(res))
}
// Configure user name and email for the repository
if err := setGitConfig(dir, "user.email", "server@plandex.ai"); err != nil {
return err
}
if err := setGitConfig(dir, "user.name", "Plandex"); err != nil {
return err
}
return nil
}
func getGitRepo(orgId, planId string) *GitRepo {
return &GitRepo{
orgId: orgId,
planId: planId,
}
}
func (repo *GitRepo) GitAddAndCommit(branch, message string) error {
log.Printf("[Git] GitAddAndCommit - orgId: %s, planId: %s, branch: %s, message: %s", repo.orgId, repo.planId, branch, message)
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
return gitAdd(dir, ".")
}, dir, fmt.Sprintf("GitAddAndCommit > gitAdd: plan=%s branch=%s", planId, branch))
if err != nil {
return fmt.Errorf("error adding files to git repository for dir: %s, err: %v", dir, err)
}
err = gitWriteOperation(func() error {
return gitCommit(dir, message)
}, dir, fmt.Sprintf("GitAddAndCommit > gitCommit: plan=%s branch=%s", planId, branch))
if err != nil {
return fmt.Errorf("error committing files to git repository for dir: %s, err: %v", dir, err)
}
// log.Println("[Git] GitAddAndCommit - finished, logging repo state")
// repo.LogGitRepoState()
return nil
}
func (repo *GitRepo) GitRewindToSha(branch, sha string) error {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
return gitRewindToSha(dir, sha)
}, dir, fmt.Sprintf("GitRewindToSha > gitRewindToSha: plan=%s branch=%s", planId, branch))
if err != nil {
return fmt.Errorf("error rewinding git repository for dir: %s, err: %v", dir, err)
}
return nil
}
func (repo *GitRepo) GetCurrentCommitSha() (sha string, err error) {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
cmd := exec.Command("git", "-C", dir, "rev-parse", "HEAD")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("error getting current commit SHA for dir: %s, err: %v", dir, err)
}
sha = strings.TrimSpace(string(output))
return sha, nil
}
func (repo *GitRepo) GetCommitTime(branch, ref string) (time.Time, error) {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
// Use git show to get the commit timestamp
cmd := exec.Command("git", "-C", dir, "show", "-s", "--format=%ct", ref)
output, err := cmd.Output()
if err != nil {
return time.Time{}, fmt.Errorf("error getting commit time for ref %s: %v", ref, err)
}
// Parse the Unix timestamp
timestamp, err := strconv.ParseInt(strings.TrimSpace(string(output)), 10, 64)
if err != nil {
return time.Time{}, fmt.Errorf("error parsing commit timestamp for ref %s: %v", ref, err)
}
// Convert Unix timestamp to time.Time
commitTime := time.Unix(timestamp, 0)
return commitTime, nil
}
func (repo *GitRepo) GitResetToSha(sha string) error {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
cmd := exec.Command("git", "-C", dir, "reset", "--hard", sha)
_, err := cmd.Output()
if err != nil {
return fmt.Errorf("error resetting git repository to SHA for dir: %s, sha: %s, err: %v", dir, sha, err)
}
return nil
}, dir, fmt.Sprintf("GitResetToSha > gitReset: plan=%s sha=%s", planId, sha))
if err != nil {
return fmt.Errorf("error resetting git repository to SHA for dir: %s, sha: %s, err: %v", dir, sha, err)
}
return nil
}
func (repo *GitRepo) GitCheckoutSha(sha string) error {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
cmd := exec.Command("git", "-C", dir, "checkout", sha)
_, err := cmd.Output()
if err != nil {
return fmt.Errorf("error checking out git repository at SHA for dir: %s, sha: %s, err: %v", dir, sha, err)
}
return nil
}, dir, fmt.Sprintf("GitCheckoutSha > gitCheckout: plan=%s sha=%s", planId, sha))
if err != nil {
return fmt.Errorf("error checking out git repository at SHA for dir: %s, sha: %s, err: %v", dir, sha, err)
}
return nil
}
func (repo *GitRepo) GetGitCommitHistory(branch string) (body string, shas []string, err error) {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
body, shas, err = getGitCommitHistory(dir)
if err != nil {
return "", nil, fmt.Errorf("error getting git history for dir: %s, err: %v", dir, err)
}
return body, shas, nil
}
func (repo *GitRepo) GetLatestCommit(branch string) (sha, body string, err error) {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
sha, body, err = getLatestCommit(dir)
if err != nil {
return "", "", fmt.Errorf("error getting latest commit for dir: %s, err: %v", dir, err)
}
return sha, body, nil
}
func (repo *GitRepo) GetLatestCommitShaBeforeTime(branch string, before time.Time) (sha string, err error) {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
log.Printf("ADMIN - GetLatestCommitShaBeforeTime - dir: %s, before: %s", dir, before.Format("2006-01-02T15:04:05Z"))
// Round up to the next second
// roundedTime := before.Add(time.Second).Truncate(time.Second)
gitFormattedTime := before.Format("2006-01-02 15:04:05+0000")
// log.Printf("ADMIN - Git formatted time: %s", gitFormattedTime)
cmd := exec.Command("git", "-C", dir, "log", "-n", "1",
"--before="+gitFormattedTime,
"--pretty=%h@@|@@%B@>>>@")
log.Printf("ADMIN - Executing command: %s", cmd.String())
res, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("error getting latest commit before time for dir: %s, err: %v, output: %s", dir, err, string(res))
}
// log.Printf("ADMIN - git log res: %s", string(res))
output := strings.TrimSpace(string(res))
// history := processGitHistoryOutput(strings.TrimSpace(string(res)))
// log.Printf("ADMIN - History: %v", history)
if output == "" {
return "", fmt.Errorf("no commits found before time: %s", before.Format("2006-01-02T15:04:05Z"))
}
sha = strings.Split(output, "@@|@@")[0]
return sha, nil
}
func (repo *GitRepo) GitListBranches() ([]string, error) {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
var out bytes.Buffer
cmd := exec.Command("git", "branch", "--format=%(refname:short)")
cmd.Dir = dir
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return nil, fmt.Errorf("error getting git branches for dir: %s, err: %v", dir, err)
}
branches := strings.Split(strings.TrimSpace(out.String()), "\n")
if len(branches) == 0 || (len(branches) == 1 && branches[0] == "") {
return []string{"main"}, nil
}
return branches, nil
}
func (repo *GitRepo) GitCreateBranch(newBranch string) error {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
res, err := exec.Command("git", "-C", dir, "checkout", "-b", newBranch).CombinedOutput()
if err != nil {
return fmt.Errorf("error creating git branch for dir: %s, err: %v, output: %s", dir, err, string(res))
}
return nil
}, dir, fmt.Sprintf("GitCreateBranch > gitCheckout: plan=%s branch=%s", planId, newBranch))
if err != nil {
return err
}
return nil
}
func (repo *GitRepo) GitDeleteBranch(branchName string) error {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
res, err := exec.Command("git", "-C", dir, "branch", "-D", branchName).CombinedOutput()
if err != nil {
return fmt.Errorf("error deleting git branch for dir: %s, err: %v, output: %s", dir, err, string(res))
}
return nil
}, dir, fmt.Sprintf("GitDeleteBranch > gitBranch: plan=%s branch=%s", planId, branchName))
if err != nil {
return err
}
return nil
}
func (repo *GitRepo) GitClearUncommittedChanges(branch string) error {
orgId := repo.orgId
planId := repo.planId
log.Printf("[Git] GitClearUncommittedChanges - orgId: %s, planId: %s, branch: %s", orgId, planId, branch)
dir := getPlanDir(orgId, planId)
// first do a lightweight git status to check if there are any uncommitted changes
// prevents heavier operations below if there are no changes (the usual case)
res, err := exec.Command("git", "status", "--porcelain").CombinedOutput()
if err != nil {
return fmt.Errorf("error checking for uncommitted changes: %v, output: %s", err, string(res))
}
// If there's output, there are uncommitted changes
hasChanges := strings.TrimSpace(string(res)) != ""
if !hasChanges {
log.Printf("[Git] GitClearUncommittedChanges - no changes to clear for plan %s", planId)
return nil
}
err = gitWriteOperation(func() error {
// Reset staged changes
log.Printf("[Git] GitClearUncommittedChanges - resetting staged changes for plan %s", planId)
res, err := exec.Command("git", "-C", dir, "reset", "--hard").CombinedOutput()
if err != nil {
return fmt.Errorf("error resetting staged changes | err: %v, output: %s", err, string(res))
}
log.Printf("[Git] GitClearUncommittedChanges - reset staged changes finished for plan %s", planId)
return nil
}, dir, fmt.Sprintf("GitClearUncommittedChanges > gitReset: plan=%s", planId))
if err != nil {
return err
}
err = gitWriteOperation(func() error {
// Clean untracked files
log.Printf("[Git] GitClearUncommittedChanges - cleaning untracked files for plan %s", planId)
res, err := exec.Command("git", "-C", dir, "clean", "-d", "-f").CombinedOutput()
if err != nil {
return fmt.Errorf("error cleaning untracked files | err: %v, output: %s", err, string(res))
}
log.Printf("[Git] GitClearUncommittedChanges - clean untracked files finished for plan %s", planId)
return nil
}, dir, fmt.Sprintf("GitClearUncommittedChanges > gitClean: plan=%s", planId))
return err
}
func (repo *GitRepo) GitCheckoutBranch(branch string) error {
orgId := repo.orgId
planId := repo.planId
dir := getPlanDir(orgId, planId)
err := gitWriteOperation(func() error {
return gitCheckoutBranch(dir, branch)
}, dir, fmt.Sprintf("GitCheckoutBranch > gitCheckout: plan=%s branch=%s", planId, branch))
if err != nil {
return err
}
return nil
}
func gitAdd(repoDir, path string) error {
if err := gitRemoveIndexLockFileIfExists(repoDir); err != nil {
return fmt.Errorf("error removing lock file before add: %v", err)
}
res, err := exec.Command("git", "-C", repoDir, "add", path).CombinedOutput()
if err != nil {
return fmt.Errorf("error adding files to git repository for dir: %s, err: %v, output: %s", repoDir, err, string(res))
}
return nil
}
func gitCommit(repoDir, commitMsg string) error {
if err := gitRemoveIndexLockFileIfExists(repoDir); err != nil {
return fmt.Errorf("error removing lock file before commit: %v", err)
}
res, err := exec.Command("git", "-C", repoDir, "commit", "-m", commitMsg).CombinedOutput()
if err != nil {
return fmt.Errorf("error committing files to git repository for dir: %s, err: %v, output: %s", repoDir, err, string(res))
}
return nil
}
func gitCheckoutBranch(repoDir, branch string) error {
log.Printf("[Git] gitCheckoutBranch - repoDir: %s, branch: %s", repoDir, branch)
if err := gitRemoveIndexLockFileIfExists(repoDir); err != nil {
return fmt.Errorf("error removing lock file before checkout: %v", err)
}
// get current branch and only checkout if it's not the same
// trying to check out the same branch will result in an error
var out bytes.Buffer
cmd := exec.Command("git", "-C", repoDir, "branch", "--show-current")
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return fmt.Errorf("error getting current git branch for dir: %s, err: %v", repoDir, err)
}
currentBranch := strings.TrimSpace(out.String())
log.Printf("[Git] gitCheckoutBranch - currentBranch: %s", currentBranch)
if currentBranch == branch {
log.Printf("[Git] gitCheckoutBranch - already on branch %s, skipping", branch)
return nil
}
log.Println("[Git] gitCheckoutBranch - checking out branch:", branch)
res, err := exec.Command("git", "-C", repoDir, "checkout", branch).CombinedOutput()
if err != nil {
return fmt.Errorf("error checking out git branch for dir: %s, err: %v, output: %s", repoDir, err, string(res))
}
return nil
}
func gitRewindToSha(repoDir, sha string) error {
res, err := exec.Command("git", "-C", repoDir, "reset", "--hard", sha).CombinedOutput()
if err != nil {
return fmt.Errorf("error executing git reset for dir: %s, sha: %s, err: %v, output: %s", repoDir, sha, err, string(res))
}
return nil
}
func getLatestCommit(dir string) (sha, body string, err error) {
var out bytes.Buffer
cmd := exec.Command("git", "log", "--pretty=%h@@|@@%at@@|@@%B@>>>@")
cmd.Dir = dir
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
return "", "", fmt.Errorf("error getting git history for dir: %s, err: %v", dir, err)
}
// Process the log output to get it in the desired format.
history := processGitHistoryOutput(strings.TrimSpace(out.String()))
first := history[0]
sha = first[0]
body = first[1]
return sha, body, nil
}
func getGitCommitHistory(dir string) (body string, shas []string, err error) {
var out bytes.Buffer
cmd := exec.Command("git", "log", "--pretty=%h@@|@@%at@@|@@%B@>>>@")
cmd.Dir = dir
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
return "", nil, fmt.Errorf("error getting git history for dir: %s, err: %v", dir, err)
}
// Process the log output to get it in the desired format.
history := processGitHistoryOutput(strings.TrimSpace(out.String()))
var output []string
for _, el := range history {
shas = append(shas, el[0])
output = append(output, el[1])
}
return strings.Join(output, "\n\n"), shas, nil
}
// processGitHistoryOutput processes the raw output from the git log command and returns a formatted string.
func processGitHistoryOutput(raw string) [][2]string {
var history [][2]string
entries := strings.Split(raw, "@>>>@") // Split entries using the custom separator.
for _, entry := range entries {
// First clean up any leading/trailing whitespace or newlines from each entry.
entry = strings.TrimSpace(entry)
// Now split the cleaned entry into its parts.
parts := strings.Split(entry, "@@|@@")
if len(parts) == 3 {
sha := parts[0]
timestampStr := parts[1]
message := strings.TrimSpace(parts[2]) // Trim whitespace from message as well.
// Extract and format timestamp.
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil {
continue // Skip entries with invalid timestamps.
}
dt := time.Unix(timestamp, 0).UTC()
formattedTs := dt.Format("Mon Jan 2, 2006 | 3:04:05pm MST")
// Prepare the header with colors.
headerColor := color.New(color.FgCyan, color.Bold)
dateColor := color.New(color.FgCyan)
// Combine sha, formatted timestamp, and message header into one string.
header := fmt.Sprintf("%s | %s", headerColor.Sprintf("📝 Update %s", sha), dateColor.Sprintf("%s", formattedTs))
// Combine header and message with a newline only if the message is not empty.
fullEntry := header
if message != "" {
fullEntry += "\n" + message
}
history = append(history, [2]string{sha, fullEntry})
}
}
return history
}
func removeLockFile(lockFilePath string) error {
_, err := os.Stat(lockFilePath)
exists := err == nil
// log.Println("index.lock file exists:", exists)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("error checking lock file: %v", err)
}
attempts := 0
for exists {
if attempts > 10 {
return fmt.Errorf("error removing index.lock file: %v after %d attempts", err, attempts)
}
log.Printf("[Git] removeLockFile - removing index.lock file: %s, attempt: %d", lockFilePath, attempts)
if err := os.Remove(lockFilePath); err != nil {
if os.IsNotExist(err) {
log.Printf("[Git] removeLockFile - %s file not found, skipping removal", lockFilePath)
return nil
}
return fmt.Errorf("error removing lock file: %v", err)
}
_, err = os.Stat(lockFilePath)
exists = err == nil
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("error checking lock file: %v", err)
}
log.Printf("[Git] removeLockFile - after removal, %s file exists: %t", lockFilePath, exists)
if exists {
log.Printf("[Git] removeLockFile - %s file still exists, retrying after delay", lockFilePath)
} else {
log.Printf("[Git] removeLockFile - %s file removed successfully", lockFilePath)
return nil
}
attempts++
time.Sleep(20 * time.Millisecond)
}
return nil
}
func gitRemoveIndexLockFileIfExists(repoDir string) error {
log.Printf("[Git] gitRemoveIndexLockFileIfExists - repoDir: %s", repoDir)
paths := []string{
filepath.Join(repoDir, ".git", "index.lock"),
filepath.Join(repoDir, ".git", "refs", "heads", "HEAD.lock"),
filepath.Join(repoDir, ".git", "HEAD.lock"),
}
errCh := make(chan error, len(paths))
for _, path := range paths {
go func(path string) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in gitRemoveIndexLockFileIfExists: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in gitRemoveIndexLockFileIfExists: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
if err := removeLockFile(path); err != nil {
errCh <- err
return
}
errCh <- nil
}(path)
}
errs := []error{}
for i := 0; i < len(paths); i++ {
err := <-errCh
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return fmt.Errorf("error removing lock files: %v", errs)
}
return nil
}
func setGitConfig(repoDir, key, value string) error {
res, err := exec.Command("git", "-C", repoDir, "config", key, value).CombinedOutput()
if err != nil {
return fmt.Errorf("error setting git config %s to %s for dir: %s, err: %v, output: %s", key, value, repoDir, err, string(res))
}
return nil
}
func gitWriteOperation(operation func() error, repoDir, label string) error {
log.Printf("[Git] gitWriteOperation - label: %s", label)
var err error
for attempt := 0; attempt < maxGitRetries; attempt++ {
if attempt > 0 {
delay := time.Duration(1<<uint(attempt-1)) * baseGitRetryDelay // Exponential backoff
time.Sleep(delay)
log.Printf("Retry attempt %d for git operation %s (delay: %v)\n", attempt+1, label, delay)
}
err = operation()
if err == nil {
return nil
}
// Check if error is retryable
if strings.Contains(err.Error(), "index.lock") || strings.Contains(err.Error(), "cannot lock ref") {
log.Printf("Git lock file error detected for %s, will retry: %v\n", label, err)
err = gitRemoveIndexLockFileIfExists(repoDir)
if err != nil {
log.Printf("error removing lock files: %v", err)
}
continue
}
// Non-retryable error
return err
}
return fmt.Errorf("operation %s failed after %d attempts: %v", label, maxGitRetries, err)
}
// LogGitRepoState prints out useful debug info about the current git repository:
// - The currently checked-out branch
// - The last few commits
// - The status (untracked changes, etc.)
// - A directory listing of refs/heads
// - A directory listing of .git/ (to spot any leftover lock files or HEAD files)
func (repo *GitRepo) LogGitRepoState() {
repoDir := getPlanDir(repo.orgId, repo.planId)
log.Println("[DEBUG] --- Git Repo State ---")
// 1. Current branch
out, err := exec.Command("git", "-C", repoDir, "branch", "--show-current").CombinedOutput()
if err != nil {
log.Printf("[DEBUG] error running `git branch --show-current`: %v, output: %s", err, string(out))
} else {
log.Printf("[DEBUG] Current branch: %s", string(out))
}
// 2. Recent commits
out, err = exec.Command("git", "-C", repoDir, "log", "--oneline", "-5").CombinedOutput()
if err != nil {
log.Printf("[DEBUG] error running `git log --oneline -5`: %v, output: %s", err, string(out))
} else {
log.Printf("[DEBUG] Recent commits:\n%s", string(out))
}
// 3. Git status
out, err = exec.Command("git", "-C", repoDir, "status", "--short", "--branch").CombinedOutput()
if err != nil {
log.Printf("[DEBUG] error running `git status`: %v, output: %s", err, string(out))
} else {
log.Printf("[DEBUG] Git status:\n%s", string(out))
}
// 4. Show all refs (to see if `.git/refs/heads/HEAD` exists)
out, err = exec.Command("git", "-C", repoDir, "show-ref").CombinedOutput()
if err != nil {
log.Printf("[DEBUG] error running `git show-ref`: %v, output: %s", err, string(out))
} else {
log.Printf("[DEBUG] All refs:\n%s", string(out))
}
// 5. Directory listing of .git/refs/heads
headsDir := filepath.Join(repoDir, ".git", "refs", "heads")
out, err = exec.Command("ls", "-l", headsDir).CombinedOutput()
if err != nil {
log.Printf("[DEBUG] error listing heads dir: %s, err: %v, output: %s", headsDir, err, string(out))
} else {
log.Printf("[DEBUG] .git/refs/heads contents:\n%s", string(out))
}
// 5a. If there's actually a HEAD file in `.git/refs/heads`, cat it.
headRefPath := filepath.Join(headsDir, "HEAD")
if _, err := os.Stat(headRefPath); err == nil {
// The file `.git/refs/heads/HEAD` exists, which is unusual
log.Printf("[DEBUG] Found .git/refs/heads/HEAD. Dumping contents:")
catOut, _ := exec.Command("cat", headRefPath).CombinedOutput()
log.Printf("[DEBUG] .git/refs/heads/HEAD contents:\n%s", string(catOut))
} else if !os.IsNotExist(err) {
log.Printf("[DEBUG] error checking for .git/refs/heads/HEAD: %v", err)
}
// 6. Directory listing of .git/ in case there's HEAD.lock or index.lock
gitDir := filepath.Join(repoDir, ".git")
out, err = exec.Command("ls", "-l", gitDir).CombinedOutput()
if err != nil {
log.Printf("[DEBUG] error listing .git dir: %s, err: %v, output: %s", gitDir, err, string(out))
} else {
log.Printf("[DEBUG] .git/ contents:\n%s", string(out))
}
// 6a. If there's a .git/HEAD file, cat it
headFilePath := filepath.Join(gitDir, "HEAD")
if _, err := os.Stat(headFilePath); err == nil {
log.Printf("[DEBUG] .git/HEAD file exists. Dumping contents:")
catOut, _ := exec.Command("cat", headFilePath).CombinedOutput()
log.Printf("[DEBUG] .git/HEAD contents:\n%s", string(catOut))
} else if !os.IsNotExist(err) {
log.Printf("[DEBUG] error checking for .git/HEAD: %v", err)
}
// 6b. Check for HEAD.lock or index.lock specifically
headLockPath := filepath.Join(gitDir, "HEAD.lock")
if _, err := os.Stat(headLockPath); err == nil {
log.Printf("[DEBUG] HEAD.lock file exists at: %s", headLockPath)
} else if !os.IsNotExist(err) {
log.Printf("[DEBUG] error checking for HEAD.lock: %v", err)
}
indexLockPath := filepath.Join(gitDir, "index.lock")
if _, err := os.Stat(indexLockPath); err == nil {
log.Printf("[DEBUG] index.lock file exists at: %s", indexLockPath)
} else if !os.IsNotExist(err) {
log.Printf("[DEBUG] error checking for index.lock: %v", err)
}
log.Println("[DEBUG] --- End Git Repo State ---")
}