Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cmd/entire/cli/strategy/content_overlap.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"log/slog"
"os"
"os/exec"
"path/filepath"

"github.com/entireio/cli/cmd/entire/cli/logging"
Expand Down Expand Up @@ -522,6 +523,13 @@ func filesWithRemainingAgentChanges(
// workingTreeMatchesCommit checks if the file on disk matches the committed blob hash.
// Returns true if the working tree is clean for this file (no remaining changes).
func workingTreeMatchesCommit(worktreeRoot, filePath string, commitHash plumbing.Hash) bool {
// Ask Git first so clean/smudge filters like core.autocrlf don't create
// phantom differences between the working tree bytes and the committed blob.
cmd := exec.CommandContext(context.Background(), "git", "-C", worktreeRoot, "diff", "--exit-code", "--quiet", "--", filePath)
if err := cmd.Run(); err == nil {
return true
}

absPath := filepath.Join(worktreeRoot, filePath)
diskContent, err := os.ReadFile(absPath) //nolint:gosec // filePath is from git status, not user input
if err != nil {
Expand Down
53 changes: 53 additions & 0 deletions cmd/entire/cli/strategy/content_overlap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package strategy
import (
"context"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
Expand Down Expand Up @@ -415,6 +416,58 @@ func TestFilesWithRemainingAgentChanges_ReplacedContent(t *testing.T) {
assert.Empty(t, remaining, "Replaced content with clean working tree should not be in remaining")
}

// TestFilesWithRemainingAgentChanges_AutocrlfNormalizedWorkingTree verifies that
// line-ending normalization does not create phantom carry-forward files.
func TestFilesWithRemainingAgentChanges_AutocrlfNormalizedWorkingTree(t *testing.T) {
t.Parallel()
dir := setupGitRepo(t)

repo, err := git.PlainOpen(dir)
require.NoError(t, err)

runGit := func(args ...string) {
t.Helper()
cmd := exec.CommandContext(context.Background(), "git", args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
require.NoError(t, err, "git %v failed: %s", args, string(out))
}

runGit("config", "core.autocrlf", "true")

shadowContent := []byte("package main\r\n\r\nimport \"fmt\"\r\n\r\nfunc main() {\r\n\tfmt.Println(\"hello world\")\n\tfmt.Println(\"goodbye world\")\n}\n")
createShadowBranchWithContent(t, repo, "crlf123", "e3b0c4", map[string][]byte{
"src/main.go": shadowContent,
})

require.NoError(t, os.MkdirAll(filepath.Join(dir, "src"), 0o755))
workingTreeContent := []byte("package main\r\n\r\nimport \"fmt\"\r\n\r\nfunc main() {\r\n\tfmt.Println(\"hello world\")\r\n\tfmt.Println(\"goodbye world\")\r\n}\r\n")
testFile := filepath.Join(dir, "src", "main.go")
require.NoError(t, os.WriteFile(testFile, workingTreeContent, 0o644))

wt, err := repo.Worktree()
require.NoError(t, err)
_, err = wt.Add("src/main.go")
require.NoError(t, err)
headCommit, err := wt.Commit("Commit normalized content", &git.CommitOptions{
Author: &object.Signature{Name: "Test", Email: "test@test.com", When: time.Now()},
})
require.NoError(t, err)

commit, err := repo.CommitObject(headCommit)
require.NoError(t, err)

shadowBranch := checkpoint.ShadowBranchNameForCommit("crlf123", "e3b0c4")
committedFiles := map[string]struct{}{"src/main.go": {}}

// Git reports no diff here even though the on-disk bytes are CRLF and the
// committed blob is LF-normalized under core.autocrlf=true.
runGit("diff", "--exit-code", "--", "src/main.go")

remaining := filesWithRemainingAgentChanges(context.Background(), repo, shadowBranch, commit, []string{"src/main.go"}, committedFiles)
assert.Empty(t, remaining, "autocrlf-only working tree differences should not be carried forward")
}

// TestFilesWithRemainingAgentChanges_NoShadowBranch tests fallback to file-level subtraction.
func TestFilesWithRemainingAgentChanges_NoShadowBranch(t *testing.T) {
t.Parallel()
Expand Down
Loading