diff --git a/go.mod b/go.mod index 84a530f596ac0a1ec0d55689f0383a570a7f0a53..8b9e87f98ca47885fe4066c593132107aaeca409 100644 --- a/go.mod +++ b/go.mod @@ -105,6 +105,7 @@ require ( github.com/charmbracelet/x/json v0.2.0 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect github.com/charmbracelet/x/windows v0.2.2 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/ebitengine/purego v0.10.0 // indirect @@ -156,6 +157,7 @@ require ( github.com/ncruces/julianday v1.0.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pierrec/lz4/v4 v4.1.25 // indirect + github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/segmentio/asm v1.2.1 // indirect diff --git a/go.sum b/go.sum index 08decf62c75efdd839dd3440aa56bf4c47379732..3e72d09a4890385325f2e8c361e20da23d2cf772 100644 --- a/go.sum +++ b/go.sum @@ -141,6 +141,8 @@ github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7X github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -303,10 +305,14 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/internal/fsext/ls.go b/internal/fsext/ls.go index afc81bc156205dcc624a3a45f693047bb5be835e..0d6653a147079316a37f9a9250b4d07dbd95d48c 100644 --- a/internal/fsext/ls.go +++ b/internal/fsext/ls.go @@ -1,6 +1,7 @@ package fsext import ( + "cmp" "errors" "log/slog" "os" @@ -12,6 +13,7 @@ import ( "github.com/charlievieth/fastwalk" "github.com/charmbracelet/crush/internal/csync" "github.com/charmbracelet/crush/internal/home" + gitconfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/format/gitignore" ) @@ -80,18 +82,52 @@ var commonIgnorePatterns = sync.OnceValue(func() []gitignore.Pattern { return parsePatterns(patterns, nil) }) -var homeIgnorePatterns = sync.OnceValue(func() []gitignore.Pattern { - homeDir := home.Dir() - var lines []string - for _, name := range []string{ - filepath.Join(homeDir, ".gitignore"), - filepath.Join(homeDir, ".config", "git", "ignore"), - filepath.Join(homeDir, ".config", "crush", "ignore"), - } { - if bts, err := os.ReadFile(name); err == nil { - lines = append(lines, strings.Split(string(bts), "\n")...) +// gitGlobalIgnorePatterns returns patterns from git's global excludes file +// (core.excludesFile), following git's config resolution order. +var gitGlobalIgnorePatterns = sync.OnceValue(func() []gitignore.Pattern { + cfg, err := gitconfig.LoadConfig(gitconfig.GlobalScope) + if err != nil { + slog.Debug("Failed to load global git config", "error", err) + return nil + } + + configPath := cmp.Or( + os.Getenv("XDG_CONFIG_HOME"), + filepath.Join(home.Dir(), ".config"), + ) + excludesFilePath := cmp.Or( + cfg.Raw.Section("core").Options.Get("excludesfile"), + filepath.Join(configPath, "git", "ignore"), + ) + excludesFilePath = home.Long(excludesFilePath) + + bts, err := os.ReadFile(excludesFilePath) + if err != nil { + if !os.IsNotExist(err) { + slog.Debug("Failed to read git global excludes file", "path", excludesFilePath, "error", err) } + return nil + } + + return parsePatterns(strings.Split(string(bts), "\n"), nil) +}) + +// crushGlobalIgnorePatterns returns patterns from the user's +// ~/.config/crush/ignore file. +var crushGlobalIgnorePatterns = sync.OnceValue(func() []gitignore.Pattern { + configPath := cmp.Or( + os.Getenv("XDG_CONFIG_HOME"), + filepath.Join(home.Dir(), ".config"), + ) + name := filepath.Join(configPath, "crush", "ignore") + bts, err := os.ReadFile(name) + if err != nil { + if !os.IsNotExist(err) { + slog.Debug("Failed to read crush global ignore file", "path", name, "error", err) + } + return nil } + lines := strings.Split(string(bts), "\n") return parsePatterns(lines, nil) }) @@ -169,8 +205,9 @@ func (dl *directoryLister) getCombinedMatcher(dir string) gitignore.Matcher { // Add common patterns first (lowest priority). allPatterns = append(allPatterns, commonIgnorePatterns()...) - // Add home ignore patterns. - allPatterns = append(allPatterns, homeIgnorePatterns()...) + // Add global ignore patterns (git core.excludesFile + crush global ignore). + allPatterns = append(allPatterns, gitGlobalIgnorePatterns()...) + allPatterns = append(allPatterns, crushGlobalIgnorePatterns()...) // Collect patterns from root to this directory. relDir, _ := filepath.Rel(dl.rootPath, dir)