gitignore

package module
v0.0.0-...-74ef740 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 29, 2020 License: MIT Imports: 10 Imported by: 0

README

go-gitignore

Package go-gitignore provides an interface for parsing .gitignore files, either individually, or within a repository, and matching paths against the retrieved patterns. Path matching is done using fnmatch as specified by git, with support for recursive matching via the ** pattern.

import "github.com/denormal/go-gitignore"

// match a file against a particular .gitignore
ignore, err := gitignore.NewFromFile("/my/.gitignore")
if err != nil {
    panic(err)
}
match := ignore.Match("/my/file/to.check")
if match != nil {
    if match.Ignore() {
        return true
    }
}

// or match against a repository
//  - here we match a directory path relative to the repository
ignore, err := gitignore.NewRepository( "/my/git/repository" )
if err != nil {
    panic(err)
}
match := ignore.Relative("src/examples", true)
if match != nil {
    if match.Include() {
        fmt.Printf(
            "include src/examples/ because of pattern %q at %s",
			match, match.Position(),
		)
    }
}

// if it's not important whether a path matches, but whether it is
// ignored or included...
if ignore.Ignore("src/test") {
    fmt.Println("ignore src/test")
} else if ignore.Include("src/github.com") {
    fmt.Println("include src/github.com")
}

For more information see godoc github.com/denormal/go-gitignore.

Patterns

go-gitignore supports the same .gitignore pattern format and matching rules as defined by git:

  • A blank line matches no files, so it can serve as a separator for readability.

  • A line starting with # serves as a comment. Put a backslash \ in front of the first hash for patterns that begin with a hash.

  • Trailing spaces are ignored unless they are quoted with backslash \.

  • An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again. It is not possible to re-include a file if a parent directory of that file is excluded. Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined. Put a backslash \ in front of the first ! for patterns that begin with a literal !, for example, \!important!.txt.

  • If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in Git).

  • If the pattern does not contain a slash /, Git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the toplevel of the work tree if not from a .gitignore file).

  • Otherwise, Git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. For example, Documentation/*.html matches Documentation/git.html but not Documentation/ppc/ppc.html or tools/perf/Documentation/perf.html.

  • A leading slash matches the beginning of the pathname. For example, /*.c matches cat-file.c but not mozilla-sha1/sha1.c.

Two consecutive asterisks ** in patterns matched against full pathname may have special meaning:

  • A leading ** followed by a slash means match in all directories. For example, **/foo matches file or directory foo anywhere, the same as pattern foo. **/foo/bar matches file or directory bar anywhere that is directly under directory foo.

  • A trailing /** matches everything inside. For example, abc/** matches all files inside directory abc, relative to the location of the .gitignore file, with infinite depth.

  • A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example, a/**/b matches a/b, a/x/b, a/x/y/b and so on.

  • Other consecutive asterisks are considered invalid.

Installation

go-gitignore can be installed using the standard Go approach:

go get github.com/denormal/go-gitignore

License

Copyright (c) 2016 Denormal Limited

MIT License

Documentation

Overview

Package gitignore provides an interface for parsing .gitignore files, either individually, or within a repository, and matching paths against the retrieved patterns. Path matching is done using fnmatch as specified by git (see https://git-scm.com/docs/gitignore), with support for recursive matching via the "**" pattern.

Index

Examples

Constants

View Source
const File = ".gitignore"

Variables

View Source
var (
	CarriageReturnError   = errors.New("unexpected carriage return '\\r'")
	InvalidPatternError   = errors.New("invalid pattern")
	InvalidDirectoryError = errors.New("invalid directory")
)

Functions

This section is empty.

Types

type Cache

type Cache interface {
	// Set stores the GitIgnore ignore against its path.
	Set(path string, ig GitIgnore)

	// Get attempts to retrieve an GitIgnore instance associated with the given
	// path. If the path is not known nil is returned.
	Get(path string) GitIgnore
}

Cache is the interface for the GitIgnore cache

func NewCache

func NewCache() Cache

NewCache returns a Cache instance. This is a thread-safe, in-memory cache for GitIgnore instances.

type Error

type Error interface {
	error

	// Position returns the position of the error within the .gitignore file
	// (if any)
	Position() Position

	// Underlying returns the underlying error, permitting direct comparison
	// against the wrapped error.
	Underlying() error
}

func NewError

func NewError(e error, p Position) Error

NewError returns a new Error instance for the given error e and position p.

type GitIgnore

type GitIgnore interface {
	// Base returns the directory containing the .gitignore file.
	Base() string

	// Match attempts to match the path against this GitIgnore, and will
	// return its Match if successful. Match will invoke the GitIgnore error
	// handler (if defined) if it is not possible to determine the absolute
	// path of the given path, or if its not possible to determine if the
	// path represents a file or a directory. If an error occurs, Match
	// returns nil and the error handler (if defined via New, NewWithErrors
	// or NewWithCache) will be invoked.
	Match(path string) Match

	// Absolute attempts to match an absolute path against this GitIgnore. If
	// the path is not located under the base directory of this GitIgnore, or
	// is not matched by this GitIgnore, nil is returned.
	Absolute(string, bool) Match

	// Relative attempts to match a path relative to the GitIgnore base
	// directory. isdir is used to indicate whether the path represents a file
	// or a directory. If the path is not matched by the GitIgnore, nil is
	// returned.
	Relative(path string, isdir bool) Match

	// Ignore returns true if the path is ignored by this GitIgnore. Paths
	// that are not matched by this GitIgnore are not ignored. Internally,
	// Ignore uses Match, and will return false if Match() returns nil for path.
	Ignore(path string) bool

	// Include returns true if the path is included by this GitIgnore. Paths
	// that are not matched by this GitIgnore are always included. Internally,
	// Include uses Match, and will return true if Match() returns nil for path.
	Include(path string) bool
}

GitIgnore is the interface to .gitignore files and repositories. It defines methods for testing files for matching the .gitignore file, and then determining whether a file should be ignored or included.

func New

func New(r io.Reader, base string, errors func(Error) bool) GitIgnore

NewGitIgnore creates a new GitIgnore instance from the patterns listed in t, representing a .gitignore file in the base directory. If errors is given, it will be invoked for every error encountered when parsing the .gitignore patterns. Parsing will terminate if errors is called and returns false, otherwise, parsing will continue until end of file has been reached.

func NewFromFile

func NewFromFile(file string) (GitIgnore, error)

NewFromFile creates a GitIgnore instance from the given file. An error will be returned if file cannot be opened or its absolute path determined.

Example
ignore, err := gitignore.NewFromFile("/my/project/.gitignore")
if err != nil {
	panic(err)
}

// attempt to match an absolute path
match := ignore.Match("/my/project/src/file.go")
if match != nil {
	if match.Ignore() {
		fmt.Println("ignore file.go")
	}
}

// attempt to match a relative path
//		- this is equivalent to the call above
match = ignore.Relative("src/file.go", false)
if match != nil {
	if match.Include() {
		fmt.Println("include file.go")
	}
}
Output:

func NewRepository

func NewRepository(base string) (GitIgnore, error)

NewRepository returns a GitIgnore instance representing a git repository with root directory base. If base is not a directory, or base cannot be read, NewRepository will return an error.

Internally, NewRepository uses NewRepositoryWithFile.

Example
ignore, err := gitignore.NewRepository("/my/project")
if err != nil {
	panic(err)
}

// attempt to match a directory in the repository
match := ignore.Relative("src/examples", true)
if match != nil {
	if match.Ignore() {
		fmt.Printf(
			"ignore src/examples because of pattern %q at %s",
			match, match.Position(),
		)
	}
}

// if we have an absolute path, or a path relative to the current
// working directory we can use the short-hand methods
if ignore.Include("/my/project/etc/service.conf") {
	fmt.Println("include the service configuration")
}
Output:

func NewRepositoryWithCache

func NewRepositoryWithCache(base, file string, cache Cache, errors func(e Error) bool) GitIgnore

NewRepositoryWithCache returns a GitIgnore instance representing a git repository with a root directory base. As with NewRepositoryWithErrors, file specifies the name of the files within the repository containing the .gitignore patterns, and defaults to ".gitignore" if file is not specified. If the ignore file name is ".gitignore", the returned GitIgnore instance will also consider patterns listed in $GIT_DIR/info/exclude when performing repository matching.

NewRepositoryWithCache will attempt to load each .gitignore within the repository only once, using NewWithCache to store the corresponding GitIgnore instance in cache. If cache is given as nil, NewRepositoryWithCache will create a Cache instance for this repository.

If errors is given, it will be invoked for each error encountered while matching a path against the repository GitIgnore (such as file permission denied, or errors during .gitignore parsing). See Match below.

func NewRepositoryWithErrors

func NewRepositoryWithErrors(base, file string, errors func(e Error) bool) GitIgnore

NewRepositoryWithErrors returns a GitIgnore instance representing a git repository with a root directory base. As with NewRepositoryWithFile, file specifies the name of the files within the repository containing the .gitignore patterns, and defaults to ".gitignore" if file is not specified. If the ignore file name is ".gitignore", the returned GitIgnore instance will also consider patterns listed in $GIT_DIR/info/exclude when performing repository matching.

If errors is given, it will be invoked for each error encountered while matching a path against the repository GitIgnore (such as file permission denied, or errors during .gitignore parsing). See Match below.

Internally, NewRepositoryWithErrors uses NewRepositoryWithCache.

func NewRepositoryWithFile

func NewRepositoryWithFile(base, file string) (GitIgnore, error)

NewRepositoryWithFile returns a GitIgnore instance representing a git repository with root directory base. The repository will use file as the name of the files within the repository from which to load the .gitignore patterns. If file is the empty string, NewRepositoryWithFile uses ".gitignore". If the ignore file name is ".gitignore", the returned GitIgnore instance will also consider patterns listed in $GIT_DIR/info/exclude when performing repository matching.

Internally, NewRepositoryWithFile uses NewRepositoryWithErrors.

func NewWithCache

func NewWithCache(file string, cache Cache, errors func(Error) bool) GitIgnore

NewWithCache returns a GitIgnore instance (using NewWithErrors) for the given file. If the file has been loaded before, its GitIgnore instance will be returned from the cache rather than being reloaded. If cache is not defined, NewWithCache will behave as NewWithErrors

If NewWithErrors returns nil, NewWithCache will store an empty GitIgnore (i.e. no patterns) against the file to prevent repeated parse attempts on subsequent requests for the same file. Subsequent calls to NewWithCache for a file that could not be loaded due to an error will return nil.

If errors is given, it will be invoked for every error encountered when parsing the .gitignore patterns. Parsing will terminate if errors is called and returns false, otherwise, parsing will continue until end of file has been reached.

func NewWithErrors

func NewWithErrors(file string, errors func(Error) bool) GitIgnore

NewWithErrors creates a GitIgnore instance from the given file. If errors is given, it will be invoked for every error encountered when parsing the .gitignore patterns. Parsing will terminate if errors is called and returns false, otherwise, parsing will continue until end of file has been reached. NewWithErrors returns nil if the .gitignore could not be read.

type Lexer

type Lexer interface {
	// Next returns the next Token from the Lexer reader. If an error is
	// encountered, it will be returned as an Error instance, detailing the
	// error and its position within the stream.
	Next() (*Token, Error)

	// Position returns the current position of the Lexer.
	Position() Position

	// String returns the string representation of the current position of the
	// Lexer.
	String() string
}

Lexer is the interface to the lexical analyser for .gitignore files

func NewLexer

func NewLexer(r io.Reader) Lexer

NewLexer returns a Lexer instance for the io.Reader r.

type Match

type Match interface {
	// Ignore returns true if the match pattern describes files or paths that
	// should be ignored.
	Ignore() bool

	// Include returns true if the match pattern describes files or paths that
	// should be included.
	Include() bool

	// String returns a string representation of the matched pattern.
	String() string

	// Position returns the position in the .gitignore file at which the
	// matching pattern was defined.
	Position() Position
}

Match represents the interface of successful matches against a .gitignore pattern set. A Match can be queried to determine whether the matched path should be ignored or included (i.e. was the path matched by a negated pattern), and to extract the position of the pattern within the .gitignore, and a string representation of the pattern.

type Parser

type Parser interface {
	// Parse returns all well-formed .gitignore Patterns contained within the
	// parser stream. Parsing will terminate at the end of the stream, or if
	// the parser error handler returns false.
	Parse() []Pattern

	// Next returns the next well-formed .gitignore Pattern from the parser
	// stream.  If an error is encountered, and the error handler is either
	// not defined, or returns true, Next will skip to the end of the current
	// line and attempt to parse the next Pattern. If the error handler
	// returns false, or the parser reaches the end of the stream, Next
	// returns nil.
	Next() Pattern

	// Position returns the current position of the parser in the input stream.
	Position() Position

} // Parser{}

Parser is the interface for parsing .gitignore files and extracting the set of patterns specified in the .gitignore file.

func NewParser

func NewParser(r io.Reader, err func(Error) bool) Parser

NewParser returns a new Parser instance for the given stream r. If err is not nil, it will be called for every error encountered during parsing. Parsing will terminate at the end of the stream, or if err returns false.

type Pattern

type Pattern interface {
	Match

	// Match returns true if the given path matches the name pattern. If the
	// pattern is meant for directories only, and the path is not a directory,
	// Match will return false. The matching is performed by fnmatch(). It
	// is assumed path is relative to the base path of the owning GitIgnore.
	Match(string, bool) bool
}

Pattern represents per-line patterns within a .gitignore file

func NewPattern

func NewPattern(tokens []*Token) Pattern

NewPattern returns a Pattern from the ordered slice of Tokens. The tokens are assumed to represent a well-formed .gitignore pattern. A Pattern may be negated, anchored to the start of the path (relative to the base directory of tie containing .gitignore), or match directories only.

type Position

type Position struct {
	File   string
	Line   int
	Column int
	Offset int
}

Position represents the position of the .gitignore parser, and the position of a .gitignore pattern within the parsed stream.

func (Position) String

func (p Position) String() string

String returns a string representation of the current position.

func (Position) Zero

func (p Position) Zero() bool

Zero returns true if the Position represents the zero Position

type Token

type Token struct {
	Type TokenType
	Word []rune
	Position
}

Token represents a parsed token from a .gitignore stream, encapsulating the token type, the runes comprising the token, and the position within the stream of the first rune of the token.

func NewToken

func NewToken(t TokenType, word []rune, pos Position) *Token

NewToken returns a Token instance of the given t, represented by the word runes, at the stream position pos. If the token type is not know, the returned instance will have type BAD.

func (*Token) Name

func (t *Token) Name() string

Name returns a string representation of the Token type.

func (*Token) String

func (t *Token) String() string

String returns a string representation of the Token, encapsulating its position in the input stream, its name (i.e. type), and its runes.

func (*Token) Token

func (t *Token) Token() string

Token returns the string representation of the Token word.

type TokenType

type TokenType int
const (
	ILLEGAL TokenType = iota
	EOF
	EOL
	WHITESPACE
	COMMENT
	SEPARATOR
	NEGATION
	PATTERN
	ANY
	BAD
)

func (TokenType) String

func (t TokenType) String() string

String returns a string representation of the Token type.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL