From d2ebe99e0ed11bc3865f0f425d19a11da3dbf372 Mon Sep 17 00:00:00 2001 From: Kayvan Sylvan Date: Sat, 3 Jan 2026 14:29:18 -0800 Subject: [PATCH] fix: use native git CLI for add/commit in worktrees go-git has issues with worktrees where the object database isn't properly shared, causing 'invalid object' errors when trying to commit. Switching to native git CLI for add and commit operations resolves this. This fixes generate_changelog failing in worktrees with errors like: - 'cannot create empty commit: clean working tree' - 'error: invalid object ... Error building trees' Co-Authored-By: Warp --- cmd/generate_changelog/internal/git/walker.go | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/cmd/generate_changelog/internal/git/walker.go b/cmd/generate_changelog/internal/git/walker.go index b5ce43f9..a8c03c8d 100644 --- a/cmd/generate_changelog/internal/git/walker.go +++ b/cmd/generate_changelog/internal/git/walker.go @@ -3,6 +3,7 @@ package git import ( "fmt" "os" + "os/exec" "path/filepath" "regexp" "strconv" @@ -485,57 +486,53 @@ func (w *Walker) GetStatusDetails() (string, error) { } // AddFile adds a file to the git index +// Uses native git CLI instead of go-git to properly handle worktree scenarios func (w *Walker) AddFile(filename string) error { worktree, err := w.repo.Worktree() if err != nil { return fmt.Errorf("failed to get worktree: %w", err) } - _, err = worktree.Add(filename) + worktreePath := worktree.Filesystem.Root() + + // Use native git add command to avoid go-git worktree issues + cmd := exec.Command("git", "add", filename) + cmd.Dir = worktreePath + + output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("failed to add file %s: %w", filename, err) + return fmt.Errorf("failed to add file %s: %w (output: %s)", filename, err, string(output)) } return nil } // CommitChanges creates a commit with the given message +// Uses native git CLI instead of go-git to properly handle worktree scenarios func (w *Walker) CommitChanges(message string) (plumbing.Hash, error) { worktree, err := w.repo.Worktree() if err != nil { return plumbing.ZeroHash, fmt.Errorf("failed to get worktree: %w", err) } - // Get git config for author information - cfg, err := w.repo.Config() + worktreePath := worktree.Filesystem.Root() + + // Use native git commit command to avoid go-git worktree issues + cmd := exec.Command("git", "commit", "-m", message) + cmd.Dir = worktreePath + + output, err := cmd.CombinedOutput() if err != nil { - return plumbing.ZeroHash, fmt.Errorf("failed to get git config: %w", err) + return plumbing.ZeroHash, fmt.Errorf("failed to commit: %w (output: %s)", err, string(output)) } - var authorName, authorEmail string - if cfg.User.Name != "" { - authorName = cfg.User.Name - } else { - authorName = "Changelog Bot" - } - if cfg.User.Email != "" { - authorEmail = cfg.User.Email - } else { - authorEmail = "bot@changelog.local" - } - - commit, err := worktree.Commit(message, &git.CommitOptions{ - Author: &object.Signature{ - Name: authorName, - Email: authorEmail, - When: time.Now(), - }, - }) + // Get the commit hash from HEAD + ref, err := w.repo.Head() if err != nil { - return plumbing.ZeroHash, fmt.Errorf("failed to commit: %w", err) + return plumbing.ZeroHash, fmt.Errorf("failed to get HEAD after commit: %w", err) } - return commit, nil + return ref.Hash(), nil } // PushToRemote pushes the current branch to the remote repository