Repository: numtide/treefmt (go)

Issue:
`treefmt` ignores files which are untracked but not gitignored
**Describe the bug**

If you have a file which is not yet tracked by git, but which is not gitignored, then `treefmt` does not format it (because it doesn't show up in `git ls-files`).

I think this could be fixed by passing `-o` to `git ls-files`.

**To Reproduce**

Steps to reproduce the behavior:

1. Add a new file
2. Don't `git add` it
3. Run `treefmt`

**Expected behavior**

I would expect it to be formatted.


Pull request description:
feat: traverse untracked files in git walker
This was quick enough to implement and, at face value, feels appropriate. 

But my spidey senses are tingling, and I can't quite understand why. 

@zimbatm, @jfly: why might this be the wrong thing to do?

Closes #557


Patch:
diff --git a/cmd/format/format.go b/cmd/format/format.go
index ca27290..e216e0d 100644
--- a/cmd/format/format.go
+++ b/cmd/format/format.go
@@ -222,7 +222,7 @@ func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string)
 // the readers will ignore symlinks.
 func resolvePaths(paths []string, walkType walk.Type, treeRoot string) error {
 	for i, path := range paths {
-		log.Debugf("Resolving path '%s': %v", path, walkType)
+		log.Errorf("Resolving path '%s': %v", path, walkType)
 
 		absolutePath, err := filepath.Abs(path)
 		if err != nil {
diff --git a/walk/git.go b/walk/git.go
index 84cbbe7..e0d4bd6 100644
--- a/walk/git.go
+++ b/walk/git.go
@@ -38,7 +38,7 @@ func (g *GitReader) Read(ctx context.Context, files []*File) (n int, err error)
 		r, w := io.Pipe()
 
 		// create a command which will execute from the specified sub path within root
-		cmd := exec.Command("git", "ls-files")
+		cmd := exec.Command("git", "ls-files", "--cached", "--others")
 		cmd.Dir = filepath.Join(g.root, g.path)
 		cmd.Stdout = w
 


Test patch:
diff --git a/cmd/root_test.go b/cmd/root_test.go
index 9dfb2c2..c5498f4 100644
--- a/cmd/root_test.go
+++ b/cmd/root_test.go
@@ -1359,11 +1359,15 @@ func TestGit(t *testing.T) {
 	as.NoError(gitCmd.Run(), "failed to init git repository")
 
 	// run before adding anything to the index
+	// we should pick up untracked files since we use `git ls-files -o`
 	treefmt(t,
 		withConfig(configPath, cfg),
 		withNoError(t),
 		withStats(t, map[stats.Type]int{
-			stats.Traversed: 0,
+			stats.Traversed: 31,
+			stats.Matched:   31,
+			stats.Formatted: 31,
+			stats.Changed:   0,
 		}),
 	)
 
@@ -1377,14 +1381,13 @@ func TestGit(t *testing.T) {
 		withStats(t, map[stats.Type]int{
 			stats.Traversed: 31,
 			stats.Matched:   31,
-			stats.Formatted: 31,
+			stats.Formatted: 0,
 			stats.Changed:   0,
 		}),
 	)
 
-	// remove python directory from the index
-	gitCmd = exec.Command("git", "rm", "--cached", "python/*")
-	as.NoError(gitCmd.Run(), "failed to remove python directory from the index")
+	// remove python directory
+	as.NoError(os.RemoveAll(filepath.Join(tempDir, "python")), "failed to remove python directory")
 
 	// we should traverse and match against fewer files, but no formatting should occur as no formatting signatures
 	// are impacted
@@ -1411,8 +1414,8 @@ func TestGit(t *testing.T) {
 		withConfig(configPath, cfg),
 		withNoError(t),
 		withStats(t, map[stats.Type]int{
-			stats.Traversed: 79,
-			stats.Matched:   79,
+			stats.Traversed: 76,
+			stats.Matched:   76,
 			stats.Formatted: 49, // the echo formatter should only be applied to the new files
 			stats.Changed:   0,
 		}),
diff --git a/walk/git_test.go b/walk/git_test.go
index 8731da2..b3e2329 100644
--- a/walk/git_test.go
+++ b/walk/git_test.go
@@ -29,12 +29,12 @@ func TestGitReader(t *testing.T) {
 	reader, err := walk.NewGitReader(tempDir, "", &statz)
 	as.NoError(err)
 
-	files := make([]*walk.File, 8)
+	files := make([]*walk.File, 32)
 	ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
 	n, err := reader.Read(ctx, files)
 
 	cancel()
-	as.Equal(0, n)
+	as.Equal(31, n)
 	as.ErrorIs(err, io.EOF)
 
 	// add everything to the git index
@@ -42,6 +42,7 @@ func TestGitReader(t *testing.T) {
 	cmd.Dir = tempDir
 	as.NoError(cmd.Run(), "failed to add everything to the index")
 
+	statz = stats.New()
 	reader, err = walk.NewGitReader(tempDir, "", &statz)
 	as.NoError(err)