diff --git a/docs/configuration.md b/docs/configuration.md index bdf21ef2..9a468817 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1259,7 +1259,9 @@ pre-commit: ### `exclude` -You can provide a regular expression to exclude some files from being passed to [`run`](#run) command. +You can provide a list of filenames or a regular expression to exclude some files from being passed to [`run`](#run) command. + +If you pass a list of filenames, they must contain the full path of the file from the project root. The regular expression is matched against full paths to files in the repo, relative to the repo root, using `/` as the directory separator on all platforms. @@ -1280,6 +1282,23 @@ pre-commit: run: bundle exec rubocop --force-exclusion {staged_files} ``` +The same example using a list of filenames. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + glob: "*.rb" + exclude: + - config/routes.rb + - config/application.rb + - spec/rails_helper.rb + run: bundle exec rubocop --force-exclusion {staged_files} +``` + + **Notes** Be careful with the config file format's string quoting and escaping rules when writing regexps in it. For YAML, single quotes are often the simplest choice. diff --git a/internal/config/command.go b/internal/config/command.go index 1e243da2..ad2db62e 100644 --- a/internal/config/command.go +++ b/internal/config/command.go @@ -23,9 +23,9 @@ type Command struct { FileTypes []string `json:"file_types,omitempty" mapstructure:"file_types" toml:"file_types,omitempty" yaml:"file_types,omitempty"` - Glob string `json:"glob,omitempty" mapstructure:"glob" toml:"glob,omitempty" yaml:",omitempty"` - Root string `json:"root,omitempty" mapstructure:"root" toml:"root,omitempty" yaml:",omitempty"` - Exclude string `json:"exclude,omitempty" mapstructure:"exclude" toml:"exclude,omitempty" yaml:",omitempty"` + Glob string `json:"glob,omitempty" mapstructure:"glob" toml:"glob,omitempty" yaml:",omitempty"` + Root string `json:"root,omitempty" mapstructure:"root" toml:"root,omitempty" yaml:",omitempty"` + Exclude interface{} `json:"exclude,omitempty" mapstructure:"exclude" toml:"exclude,omitempty" yaml:",omitempty"` Priority int `json:"priority,omitempty" mapstructure:"priority" toml:"priority,omitempty" yaml:",omitempty"` FailText string `json:"fail_text,omitempty" mapstructure:"fail_text" toml:"fail_text,omitempty" yaml:"fail_text,omitempty"` diff --git a/internal/lefthook/runner/filters/filters.go b/internal/lefthook/runner/filters/filters.go index b82cfe71..932b9700 100644 --- a/internal/lefthook/runner/filters/filters.go +++ b/internal/lefthook/runner/filters/filters.go @@ -62,18 +62,42 @@ func byGlob(vs []string, matcher string) []string { return vsf } -func byExclude(vs []string, matcher string) []string { - if matcher == "" { +func byExclude(vs []string, matcher interface{}) []string { + switch exclude := matcher.(type) { + case nil: return vs - } + case string: + if len(exclude) == 0 { + return vs + } - vsf := make([]string, 0) - for _, v := range vs { - if res, _ := regexp.MatchString(matcher, v); !res { - vsf = append(vsf, v) + vsf := make([]string, 0) + for _, v := range vs { + if res, _ := regexp.MatchString(exclude, v); !res { + vsf = append(vsf, v) + } + } + + return vsf + case []interface{}: + excludeNames := make(map[string]struct{}, len(exclude)) + for _, name := range exclude { + excludeNames[name.(string)] = struct{}{} + } + + vsf := make([]string, 0) + for _, v := range vs { + if _, excluded := excludeNames[v]; !excluded { + vsf = append(vsf, v) + } } + + return vsf } - return vsf + + log.Warn("invalid value for exclude option") + + return vs } func byRoot(vs []string, matcher string) []string { diff --git a/testdata/exclude.txt b/testdata/exclude.txt new file mode 100644 index 00000000..9904b4af --- /dev/null +++ b/testdata/exclude.txt @@ -0,0 +1,42 @@ +exec git init +exec lefthook install +exec git config user.email "you@example.com" +exec git config user.name "Your Name" +exec git add -A +exec lefthook run -f all +stdout 'excluded.txt lefthook.yml not-excluded.txt' +exec lefthook run -f regexp +stdout 'lefthook.yml not-excluded.txt' +exec lefthook run -f array +stdout 'lefthook.yml not-excluded.txt' + +-- lefthook.yml -- +skip_output: + - skips + - meta + - summary + - execution_info +all: + commands: + echo: + run: echo {staged_files} + +regexp: + commands: + echo: + run: echo {staged_files} + exclude: '^excluded.txt' + +array: + commands: + echo: + run: echo {staged_files} + exclude: + - exclude.txt + +-- not-excluded.txt -- +sometext + +-- excluded.txt -- +somecode +