Skip to content

Commit

Permalink
feat: simplify git walking
Browse files Browse the repository at this point in the history
Replaces the use of `go-git` with a simple `git ls-file` for traversing the git index.

Signed-off-by: Brian McGee <[email protected]>
  • Loading branch information
brianmcgee committed Oct 14, 2024
1 parent 93c8343 commit 844f3fd
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 571 deletions.
191 changes: 104 additions & 87 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"testing"
"time"

"github.com/numtide/treefmt/walk"

"github.com/numtide/treefmt/cmd"

"github.com/numtide/treefmt/config"
Expand All @@ -25,11 +27,6 @@ import (

"github.com/numtide/treefmt/test"

"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/storage/filesystem"

"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -733,7 +730,7 @@ func TestBustCacheOnFormatterChange(t *testing.T) {
})
}

func TestGitWorktree(t *testing.T) {
func TestGit(t *testing.T) {
as := require.New(t)

tempDir := test.TempExamples(t)
Expand All @@ -751,20 +748,6 @@ func TestGitWorktree(t *testing.T) {

test.WriteConfig(t, configPath, cfg)

// init a git repo
repo, err := git.Init(
filesystem.NewStorage(
osfs.New(path.Join(tempDir, ".git")),
cache.NewObjectLRUDefault(),
),
osfs.New(tempDir),
)
as.NoError(err, "failed to init git repository")

// get worktree
wt, err := repo.Worktree()
as.NoError(err, "failed to get git worktree")

run := func(traversed int32, matched int32, formatted int32, changed int32) {
_, statz, err := treefmt(t, "-c", "--config-file", configPath, "--tree-root", tempDir)
as.NoError(err)
Expand All @@ -777,30 +760,43 @@ func TestGitWorktree(t *testing.T) {
})
}

// run before adding anything to the worktree
// init a git repo
gitCmd := exec.Command("git", "init")
gitCmd.Dir = tempDir
as.NoError(gitCmd.Run(), "failed to init git repository")

// run before adding anything to the index
run(0, 0, 0, 0)

// add everything to the worktree
as.NoError(wt.AddGlob("."))
as.NoError(err)
// add everything to the index
gitCmd = exec.Command("git", "add", ".")
gitCmd.Dir = tempDir
as.NoError(gitCmd.Run(), "failed to add everything to the index")

run(32, 32, 32, 0)

// remove python directory from the worktree
as.NoError(wt.RemoveGlob("python/*"))
// remove python directory from the index
gitCmd = exec.Command("git", "rm", "--cached", "python/*")
gitCmd.Dir = tempDir
as.NoError(gitCmd.Run(), "failed to remove python directory from the index")

run(29, 29, 29, 0)

// remove nixpkgs.toml from the filesystem but leave it in the index
as.NoError(os.Remove(filepath.Join(tempDir, "nixpkgs.toml")))
run(28, 28, 28, 0)

// walk with filesystem instead of git
// we should traverse more files since we will look in the .git folder
// walk with filesystem instead of with git
// the .git folder contains 49 additional files
// when added to the 31 we started with (32 minus nixpkgs.toml which we removed from the filesystem), we should
// traverse 80 files.
_, statz, err := treefmt(t, "-c", "--config-file", configPath, "--tree-root", tempDir, "--walk", "filesystem")
as.NoError(err)

assertStats(t, as, statz, map[stats.Type]int32{
stats.Traversed: 60,
stats.Matched: 60,
stats.Traversed: 80,
stats.Matched: 80,
stats.Formatted: 80,
stats.Changed: 0,
})

Expand Down Expand Up @@ -1109,65 +1105,86 @@ func TestDeterministicOrderingInPipeline(t *testing.T) {
func TestRunInSubdir(t *testing.T) {
as := require.New(t)

// capture current cwd, so we can replace it after the test is finished
cwd, err := os.Getwd()
as.NoError(err)

t.Cleanup(func() {
// return to the previous working directory
as.NoError(os.Chdir(cwd))
})

tempDir := test.TempExamples(t)
configPath := filepath.Join(tempDir, "/treefmt.toml")

// Also test that formatters are resolved relative to the treefmt root
echoPath, err := exec.LookPath("echo")
as.NoError(err)
echoRel := path.Join(tempDir, "echo")
err = os.Symlink(echoPath, echoRel)
as.NoError(err)

// change working directory to sub directory
as.NoError(os.Chdir(filepath.Join(tempDir, "elm")))
// Run the same test for each walk type
for _, walkType := range walk.TypeValues() {
t.Run(walkType.String(), func(t *testing.T) {
// capture current cwd, so we can replace it after the test is finished
cwd, err := os.Getwd()
as.NoError(err)

t.Cleanup(func() {
// return to the previous working directory
as.NoError(os.Chdir(cwd))
})

tempDir := test.TempExamples(t)
configPath := filepath.Join(tempDir, "/treefmt.toml")

// set the walk type via environment variable
t.Setenv("TREEFMT_WALK_TYPE", walkType.String())

// if we are testing git walking, init a git repo before continuing
if walkType == walk.Git {
// init a git repo
gitCmd := exec.Command("git", "init")
gitCmd.Dir = tempDir
as.NoError(gitCmd.Run(), "failed to init git repository")

// add everything to the index
gitCmd = exec.Command("git", "add", ".")
gitCmd.Dir = tempDir
as.NoError(gitCmd.Run(), "failed to add everything to the index")
}

// basic config
cfg := &config.Config{
FormatterConfigs: map[string]*config.Formatter{
"echo": {
Command: "./echo",
Includes: []string{"*"},
},
},
// test that formatters are resolved relative to the treefmt root
echoPath, err := exec.LookPath("echo")
as.NoError(err)
echoRel := path.Join(tempDir, "echo")
err = os.Symlink(echoPath, echoRel)
as.NoError(err)

// change working directory to subdirectory
as.NoError(os.Chdir(filepath.Join(tempDir, "elm")))

// basic config
cfg := &config.Config{
FormatterConfigs: map[string]*config.Formatter{
"echo": {
Command: "./echo",
Includes: []string{"*"},
},
},
}
test.WriteConfig(t, configPath, cfg)

// without any path args, should reformat the whole tree
_, statz, err := treefmt(t)
as.NoError(err)

assertStats(t, as, statz, map[stats.Type]int32{
stats.Traversed: 32,
stats.Matched: 32,
stats.Formatted: 32,
stats.Changed: 0,
})

// specify some explicit paths, relative to the tree root
// this should not work, as we're in a subdirectory
_, _, err = treefmt(t, "-c", "elm/elm.json", "haskell/Nested/Foo.hs")
as.ErrorContains(err, "path elm/elm.json not found")

// specify some explicit paths, relative to the current directory
_, statz, err = treefmt(t, "-c", "elm.json", "../haskell/Nested/Foo.hs")
as.NoError(err)

assertStats(t, as, statz, map[stats.Type]int32{
stats.Traversed: 2,
stats.Matched: 2,
stats.Formatted: 2,
stats.Changed: 0,
})
})
}
test.WriteConfig(t, configPath, cfg)

// without any path args, should reformat the whole tree
_, statz, err := treefmt(t)
as.NoError(err)

assertStats(t, as, statz, map[stats.Type]int32{
stats.Traversed: 32,
stats.Matched: 32,
stats.Formatted: 32,
stats.Changed: 0,
})

// specify some explicit paths, relative to the tree root
// this should not work, as we're in a subdirectory
_, _, err = treefmt(t, "-c", "elm/elm.json", "haskell/Nested/Foo.hs")
as.ErrorContains(err, "path elm/elm.json not found")

// specify some explicit paths, relative to the current directory
_, statz, err = treefmt(t, "-c", "elm.json", "../haskell/Nested/Foo.hs")
as.NoError(err)

assertStats(t, as, statz, map[stats.Type]int32{
stats.Traversed: 2,
stats.Matched: 2,
stats.Formatted: 2,
stats.Changed: 0,
})
}

func treefmt(t *testing.T, args ...string) ([]byte, *stats.Stats, error) {
Expand Down
4 changes: 2 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ func SetFlags(fs *pflag.FlagSet) {
)
fs.String(
"walk", "auto",
"The method used to traverse the files within the tree root. Currently supports 'auto', 'git' or "+
"'filesystem'. (env $TREEFMT_WALK)",
"The method used to traverse the files within the tree root. Currently supports "+
"<auto|git|filesystem>. (env $TREEFMT_WALK)",
)
fs.StringP(
"working-dir", "C", ".",
Expand Down
20 changes: 1 addition & 19 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ require (
github.com/BurntSushi/toml v1.4.0
github.com/adrg/xdg v0.5.0
github.com/charmbracelet/log v0.4.0
github.com/go-git/go-billy/v5 v5.5.1-0.20241008101053-371e232676ac
github.com/go-git/go-git/v5 v5.12.1-0.20240930111449-d1843220b6ab
github.com/gobwas/glob v0.2.3
github.com/otiai10/copy v1.14.0
github.com/spf13/cobra v1.8.1
Expand All @@ -21,23 +19,13 @@ require (
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/lipgloss v0.10.0 // indirect
github.com/cloudflare/circl v1.3.8 // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand All @@ -47,28 +35,22 @@ require (
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 844f3fd

Please sign in to comment.