diff --git a/filepathfilter/filepathfilter.go b/filepathfilter/filepathfilter.go index c3fe4bc3..11e1acb9 100644 --- a/filepathfilter/filepathfilter.go +++ b/filepathfilter/filepathfilter.go @@ -4,7 +4,7 @@ import ( "path/filepath" "strings" - "github.com/git-lfs/wildmatch" + "github.com/git-lfs/wildmatch/v2" "github.com/rubyist/tracerx" ) diff --git a/git/gitattr/attr.go b/git/gitattr/attr.go index 4c2ef2c1..9c4adf78 100644 --- a/git/gitattr/attr.go +++ b/git/gitattr/attr.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/git-lfs/git-lfs/v2/errors" - "github.com/git-lfs/wildmatch" + "github.com/git-lfs/wildmatch/v2" ) const attrPrefix = "[attr]" diff --git a/git/gitattr/tree_test.go b/git/gitattr/tree_test.go index d1d64d65..ca1717c9 100644 --- a/git/gitattr/tree_test.go +++ b/git/gitattr/tree_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/git-lfs/gitobj/v2" - "github.com/git-lfs/wildmatch" + "github.com/git-lfs/wildmatch/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/go.mod b/go.mod index 4b06affd..571fb5ff 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/git-lfs/gitobj/v2 v2.0.2 github.com/git-lfs/go-netrc v0.0.0-20180525200031-e0e9ca483a18 github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825 - github.com/git-lfs/wildmatch v1.0.4 + github.com/git-lfs/wildmatch/v2 v2.0.1 github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mattn/go-isatty v0.0.4 github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 diff --git a/go.sum b/go.sum index e6c62634..a0995f57 100644 --- a/go.sum +++ b/go.sum @@ -7,16 +7,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dpotapov/go-spnego v0.0.0-20210315154721-298b63a54430 h1:oempk9HjNt6rVKyKmpdnoN7XABQv3SXLWu3pxUI7Vlk= github.com/dpotapov/go-spnego v0.0.0-20210315154721-298b63a54430/go.mod h1:AVSs/gZKt1bOd2AhkhbS7Qh56Hv7klde22yXVbwYJhc= -github.com/git-lfs/gitobj/v2 v2.0.1 h1:mUGOWP+fU36rs7TY7a5Lol9FuockOBjPFUW/lwOM7Mo= -github.com/git-lfs/gitobj/v2 v2.0.1/go.mod h1:q6aqxl6Uu3gWsip5GEKpw+7459F97er8COmU45ncAxw= github.com/git-lfs/gitobj/v2 v2.0.2 h1:p8rWlhEyiSsC+4Qc+EdufySatf8sDtvJIrHAHgf7Ar8= github.com/git-lfs/gitobj/v2 v2.0.2/go.mod h1:q6aqxl6Uu3gWsip5GEKpw+7459F97er8COmU45ncAxw= github.com/git-lfs/go-netrc v0.0.0-20180525200031-e0e9ca483a18 h1:7Th0eBA4rT8WJNiM1vppjaIv9W5WJinhpbCJvRJxloI= github.com/git-lfs/go-netrc v0.0.0-20180525200031-e0e9ca483a18/go.mod h1:70O4NAtvWn1jW8V8V+OKrJJYcxDLTmIozfi2fmSz5SI= github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825 h1:riQhgheTL7tMF4d5raz9t3+IzoR1i1wqxE1kZC6dY+U= github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A= -github.com/git-lfs/wildmatch v1.0.4 h1:Mj6LPnNZ6QSHLAAPDCH596pu6A/Z1xVm2Vk0+s3CtkY= -github.com/git-lfs/wildmatch v1.0.4/go.mod h1:SdHAGnApDpnFYQ0vAxbniWR0sn7yLJ3QXo9RRfhn2ew= +github.com/git-lfs/wildmatch/v2 v2.0.1 h1:Ds+aobrV5bK0wStILUOn9irllPyf9qrFETbKzwzoER8= +github.com/git-lfs/wildmatch/v2 v2.0.1/go.mod h1:EVqonpk9mXbREP3N8UkwoWdrF249uHpCUo5CPXY81gw= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= diff --git a/vendor/github.com/git-lfs/wildmatch/LICENSE.md b/vendor/github.com/git-lfs/wildmatch/v2/LICENSE.md similarity index 100% rename from vendor/github.com/git-lfs/wildmatch/LICENSE.md rename to vendor/github.com/git-lfs/wildmatch/v2/LICENSE.md diff --git a/vendor/github.com/git-lfs/wildmatch/README.md b/vendor/github.com/git-lfs/wildmatch/v2/README.md similarity index 100% rename from vendor/github.com/git-lfs/wildmatch/README.md rename to vendor/github.com/git-lfs/wildmatch/v2/README.md diff --git a/vendor/github.com/git-lfs/wildmatch/v2/SECURITY.md b/vendor/github.com/git-lfs/wildmatch/v2/SECURITY.md new file mode 100644 index 00000000..93dd038f --- /dev/null +++ b/vendor/github.com/git-lfs/wildmatch/v2/SECURITY.md @@ -0,0 +1,4 @@ +Please see +[SECURITY.md](https://github.com/git-lfs/git-lfs/blob/master/SECURITY.md) +in the main Git LFS repository for information on how to report security +vulnerabilities in this package. diff --git a/vendor/github.com/git-lfs/wildmatch/v2/go.mod b/vendor/github.com/git-lfs/wildmatch/v2/go.mod new file mode 100644 index 00000000..8ade584f --- /dev/null +++ b/vendor/github.com/git-lfs/wildmatch/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/git-lfs/wildmatch/v2 + +go 1.15 diff --git a/vendor/github.com/git-lfs/wildmatch/package.go b/vendor/github.com/git-lfs/wildmatch/v2/package.go similarity index 100% rename from vendor/github.com/git-lfs/wildmatch/package.go rename to vendor/github.com/git-lfs/wildmatch/v2/package.go diff --git a/vendor/github.com/git-lfs/wildmatch/wildmatch.go b/vendor/github.com/git-lfs/wildmatch/v2/wildmatch.go similarity index 79% rename from vendor/github.com/git-lfs/wildmatch/wildmatch.go rename to vendor/github.com/git-lfs/wildmatch/v2/wildmatch.go index 265c6093..57027d62 100644 --- a/vendor/github.com/git-lfs/wildmatch/wildmatch.go +++ b/vendor/github.com/git-lfs/wildmatch/v2/wildmatch.go @@ -28,6 +28,19 @@ var ( w.caseFold = true } + // GitAttributes augments the functionality of the matching algorithm + // to match behavior of git when working with .gitattributes files. + GitAttributes opt = func(w *Wildmatch) { + w.gitattributes = true + } + + // Contents indicates that if a pattern matches a directory that is a + // parent of a path, then that path is included. This is the behavior + // of patterns for .gitignore. + Contents opt = func(w *Wildmatch) { + w.contents = true + } + // SystemCase either folds or does not fold filepaths and patterns, // according to whether or not the operating system on which Wildmatch // runs supports case sensitive files or not. @@ -55,6 +68,20 @@ type Wildmatch struct { // caseFold allows the instance Wildmatch to match patterns with the // same character but different case structures. caseFold bool + + // gitattributes flag indicates that logic specific to the .gitattributes file + // should be used. The two main differences are that negative expressions are + // not allowed and directories are not matched. + gitattributes bool + + // contents indicates that if a pattern matches a directory that is a + // parent of a path, then that path is included. This is the behavior + // of patterns for .gitignore. + contents bool +} + +type MatchOpts struct { + IsDirectory bool } // NewWildmatch constructs a new Wildmatch instance which matches filepaths @@ -78,7 +105,7 @@ func NewWildmatch(p string, opts ...opt) *Wildmatch { if len(parts) > 1 { w.basename = false } - w.ts = parseTokens(parts) + w.ts = w.parseTokens(parts) return w } @@ -126,16 +153,62 @@ func escapable(c byte) bool { // parseTokens parses a separated list of patterns into a sequence of // representative Tokens that will compose the pattern when applied in sequence. -func parseTokens(dirs []string) []token { +func (w *Wildmatch) parseTokens(dirs []string) []token { + if len(dirs) == 0 { + return make([]token, 0) + } + + var finalComponents []token + + if !w.gitattributes { + trailingIsEmpty := len(dirs) > 1 && dirs[len(dirs)-1] == "" + numNonEmptyDirs := len(dirs) + if trailingIsEmpty { + numNonEmptyDirs -= 1 + } + if w.contents { + finalComponents = []token{&trailingComponents{}} + if trailingIsEmpty { + // Strip off the trailing empty string. + dirs = dirs[:numNonEmptyDirs] + } + } + // If we have one component, ignoring trailing empty + // components and we know that a directory is permissible… + if numNonEmptyDirs == 1 && (trailingIsEmpty || w.contents) { + // We don't have a slash in the middle, so this can go + // anywhere in the hierarchy. If there had been a slash + // here, it would have been anchored at the root. + rest := w.parseTokensSimple(dirs) + tokens := append([]token{&unanchoredDirectory{ + Until: rest[0], + }}) + // If we're not matching all contents, then do include + // the empty component so we don't match + // non-directories. + if finalComponents == nil && len(rest) > 1 { + finalComponents = rest[1:] + } + return append(tokens, finalComponents...) + } + } + components := w.parseTokensSimple(dirs) + return append(components, finalComponents...) +} + +func (w *Wildmatch) parseTokensSimple(dirs []string) []token { if len(dirs) == 0 { return make([]token, 0) } switch dirs[0] { case "": - return parseTokens(dirs[1:]) + if len(dirs) == 1 { + return []token{&component{fns: []componentFn{substring("")}}} + } + return w.parseTokensSimple(dirs[1:]) case "**": - rest := parseTokens(dirs[1:]) + rest := w.parseTokensSimple(dirs[1:]) if len(rest) == 0 { // If there are no remaining tokens, return a lone // doubleStar token. @@ -155,7 +228,7 @@ func parseTokens(dirs []string) []token { // continue on. return append([]token{&component{ fns: parseComponent(dirs[0]), - }}, parseTokens(dirs[1:])...) + }}, w.parseTokensSimple(dirs[1:])...) } } @@ -172,7 +245,15 @@ func nonEmpty(all []string) (ne []string) { // Match returns true if and only if the pattern matched by the receiving // Wildmatch matches the entire filepath "t". func (w *Wildmatch) Match(t string) bool { - dirs, ok := w.consume(t) + dirs, ok := w.consume(t, MatchOpts{}) + if !ok { + return false + } + return len(dirs) == 0 +} + +func (w *Wildmatch) MatchWithOpts(t string, opt MatchOpts) bool { + dirs, ok := w.consume(t, opt) if !ok { return false } @@ -182,7 +263,7 @@ func (w *Wildmatch) Match(t string) bool { // consume performs the inner match of "t" against the receiver's pattern, and // returns a slice of remaining directory paths, and whether or not there was a // disagreement while matching. -func (w *Wildmatch) consume(t string) ([]string, bool) { +func (w *Wildmatch) consume(t string, opt MatchOpts) ([]string, bool) { if w.basename { // If the receiving Wildmatch has basename set, the pattern // matches only the basename of the given "t". @@ -197,8 +278,24 @@ func (w *Wildmatch) consume(t string) ([]string, bool) { t = strings.ToLower(t) } + var isDir bool + if opt.IsDirectory { + isDir = true + // Standardize the formation of subject string so directories always + // end with '/' + if !strings.HasSuffix(t, "/") { + t = t + "/" + } + } else { + isDir = strings.HasSuffix(t, string(sep)) + } + dirs := strings.Split(t, string(sep)) - isDir := strings.HasSuffix(t, string(sep)) + + // Git-attribute style matching can never match a directory + if w.gitattributes && isDir { + return dirs, false + } // Match each directory token-wise, allowing each token to consume more // than one directory in the case of the '**' pattern. @@ -213,6 +310,11 @@ func (w *Wildmatch) consume(t string) ([]string, bool) { return dirs, false } } + // If this is a directory that we've otherwise matched and all we have + // left is an empty path component, then this is a match. + if isDir && len(dirs) == 1 && len(dirs[0]) == 0 { + return nil, true + } return dirs, true } @@ -255,11 +357,16 @@ type token interface { // doubleStar is an implementation of the Token interface which greedily matches // one-or-more path components until a successor token. type doubleStar struct { - Until token + Until token + EmptyPath bool } // Consume implements token.Consume as above. func (d *doubleStar) Consume(path []string, isDir bool) ([]string, bool) { + if len(path) == 0 { + return path, d.EmptyPath + } + // If there are no remaining tokens to match, allow matching the entire // path. if d.Until == nil { @@ -286,6 +393,43 @@ func (d *doubleStar) String() string { return fmt.Sprintf("**/%s", d.Until.String()) } +// unanchoredDirectory is an implementation of the Token interface which +// greedily matches one-or-more path components until a successor token. +type unanchoredDirectory struct { + Until token +} + +// Consume implements token.Consume as above. +func (d *unanchoredDirectory) Consume(path []string, isDir bool) ([]string, bool) { + // This matches the same way as a doubleStar, so just use that + // implementation. + s := &doubleStar{Until: d.Until} + return s.Consume(path, isDir) +} + +// String implements Component.String. +func (d *unanchoredDirectory) String() string { + return fmt.Sprintf("%s/", d.Until.String()) +} + +// trailingComponents is an implementation of the Token interface which +// greedily matches any trailing components, even if empty. +type trailingComponents struct { +} + +// Consume implements token.Consume as above. +func (d *trailingComponents) Consume(path []string, isDir bool) ([]string, bool) { + // This matches the same way as a doubleStar, so just use that + // implementation. + s := &doubleStar{Until: nil, EmptyPath: true} + return s.Consume(path, isDir) +} + +// String implements Component.String. +func (d *trailingComponents) String() string { + return "" +} + // componentFn is a functional type designed to match a single component of a // directory structure by reducing the unmatched part, and returning whether or // not a match was successful. @@ -540,7 +684,7 @@ func (c *component) Consume(path []string, isDir bool) ([]string, bool) { // Components can not match directories. If we were matching the // last path in a tree structure, we can only match if it // _wasn't_ a directory. - return path[1:], !isDir + return path[1:], true } return path[1:], true diff --git a/vendor/github.com/git-lfs/wildmatch/wildmatch_casefold.go b/vendor/github.com/git-lfs/wildmatch/v2/wildmatch_casefold.go similarity index 100% rename from vendor/github.com/git-lfs/wildmatch/wildmatch_casefold.go rename to vendor/github.com/git-lfs/wildmatch/v2/wildmatch_casefold.go diff --git a/vendor/github.com/git-lfs/wildmatch/wildmatch_nocasefold.go b/vendor/github.com/git-lfs/wildmatch/v2/wildmatch_nocasefold.go similarity index 100% rename from vendor/github.com/git-lfs/wildmatch/wildmatch_nocasefold.go rename to vendor/github.com/git-lfs/wildmatch/v2/wildmatch_nocasefold.go diff --git a/vendor/modules.txt b/vendor/modules.txt index 2ed106dc..baf04f10 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -17,8 +17,8 @@ github.com/git-lfs/gitobj/v2/storage github.com/git-lfs/go-netrc/netrc # github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825 github.com/git-lfs/pktline -# github.com/git-lfs/wildmatch v1.0.4 -github.com/git-lfs/wildmatch +# github.com/git-lfs/wildmatch/v2 v2.0.1 +github.com/git-lfs/wildmatch/v2 # github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-uuid # github.com/inconshreveable/mousetrap v1.0.0