Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve git walking #439

Merged
merged 2 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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) {
brianmcgee marked this conversation as resolved.
Show resolved Hide resolved
func TestGit(t *testing.T) {
brianmcgee marked this conversation as resolved.
Show resolved Hide resolved
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,
brianmcgee marked this conversation as resolved.
Show resolved Hide resolved
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