dotignore

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2025 License: MIT Imports: 8 Imported by: 2

README

build

go-dotignore

go-dotignore is a powerful Go library for parsing .gitignore-style files and matching file paths against specified ignore patterns. It provides full support for advanced ignore rules, negation patterns, and wildcards, making it an ideal choice for file exclusion in Go projects.

Features

  • Parse .gitignore-style files seamlessly
  • Negation patterns (!) to override ignore rules
  • Support for directories, files, and advanced wildcards like **
  • Compatible with custom ignore files
  • Does not process nested .gitignore files; all patterns are treated from a single source
  • Fully compliant with the .gitignore specification
  • Lightweight API built on Go best practices

Installation

To install go-dotignore in your Go project, run:

go get github.com/codeglyph/go-dotignore

Getting Started

Example: Basic Usage

Here is a simple example of how to use go-dotignore:

package main

import (
 "fmt"
 "log"
 "github.com/codeglyph/go-dotignore"
)

func main() {
 // Define ignore patterns
 patterns := []string{
  "*.log",
  "!important.log",
  "temp/",
 }

 // Create a new pattern matcher
 matcher, err := dotignore.NewPatternMatcher(patterns)
 if err != nil {
  log.Fatalf("Failed to create pattern matcher: %v", err)
 }

 // Check if a file matches the patterns
 isIgnored, err := matcher.Matches("debug.log")
 if err != nil {
  log.Fatalf("Error matching file: %v", err)
 }
 fmt.Printf("Should ignore 'debug.log': %v\n", isIgnored)

 isIgnored, err = matcher.Matches("important.log")
 if err != nil {
  log.Fatalf("Error matching file: %v", err)
 }
 fmt.Printf("Should ignore 'important.log': %v\n", isIgnored)
}
Example: Parsing a File

To parse patterns from a file, use the NewPatternMatcherFromFile method:

package main

import (
 "log"
 "github.com/codeglyph/go-dotignore"
)

func main() {
 matcher, err := dotignore.NewPatternMatcherFromFile(".ignore")
 if err != nil {
  log.Fatalf("Failed to parse ignore file: %v", err)
 }

 isIgnored, err := matcher.Matches("example.txt")
 if err != nil {
  log.Fatalf("Error matching file: %v", err)
 }
 log.Printf("Should ignore 'example.txt': %v", isIgnored)
}
Example: Parsing from Reader

To parse patterns from an io.Reader, use the NewPatternMatcherFromReader method:

package main

import (
 "bytes"
 "log"
 "github.com/codeglyph/go-dotignore"
)

func main() {
 reader := bytes.NewBufferString("**/temp\n!keep/")
 matcher, err := dotignore.NewPatternMatcherFromReader(reader)
 if err != nil {
  log.Fatalf("Failed to parse patterns from reader: %v", err)
 }

 isIgnored, err := matcher.Matches("temp/file.txt")
 if err != nil {
  log.Fatalf("Error matching file: %v", err)
 }
 log.Printf("Should ignore 'temp/file.txt': %v", isIgnored)
}

Advanced Features

Negation Patterns

Negation patterns (!) allow you to override ignore rules. For example:

  • *.log ignores all .log files.
  • !important.log includes important.log even though .log files are ignored.
Wildcard Support
  • * matches any string except /.
  • ? matches any single character except /.
  • ** matches any number of directories.
Directory Matching
  • dir/ matches only directories named dir.
  • dir/** matches everything inside dir recursively.
Custom Ignore Files

go-dotignore supports custom ignore files. Simply provide the file path or patterns programmatically using NewPatternMatcherFromFile or NewPatternMatcher.

Non-Nested Processing

The library does not automatically process nested .gitignore files or directory-level .ignore files. All patterns are treated as coming from a single source file or list.

Specification Compliance

go-dotignore follows the .gitignore specification closely, ensuring consistent behavior with Git's pattern matching rules.

Contributing

We welcome contributions to go-dotignore! Here's how you can contribute:

  1. Fork the repository.
  2. Create a new branch for your changes.
  3. Add tests for your changes (if applicable).
  4. Run all tests to ensure nothing breaks.
  5. Submit a pull request.
Issue Templates

We encourage you to follow these templates when creating an issue:

Bug Report
  • Title: A short and descriptive title.
  • Description: What went wrong? Include steps to reproduce the issue.
  • Expected Behavior: What you expected to happen.
  • Environment: Include Go version, OS, and library version.
  • Additional Context: Add any logs, error messages, or relevant information.
Feature Request
  • Title: A concise summary of the feature.
  • Description: What problem does this feature solve?
  • Proposed Solution: How should the feature work?
  • Additional Context: Provide any mockups, examples, or supporting details.
Pull Request Guidelines
  • Ensure your code passes all tests.
  • Write clear and concise commit messages.
  • Provide a description of the change and link to related issues (if any).

License

This project is licensed under the MIT License. See the LICENSE file for more details.

Acknowledgements

This library is inspired by Git's .gitignore pattern matching and aims to bring the same functionality to Go projects.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type PatternMatcher

type PatternMatcher struct {
	// contains filtered or unexported fields
}

PatternMatcher provides methods to parse, store, and evaluate ignore patterns against file paths.

func NewPatternMatcher

func NewPatternMatcher(patterns []string) (*PatternMatcher, error)

NewPatternMatcher initializes a new PatternMatcher instance from a list of string patterns.

Example
package main

import (
	"fmt"
	"log"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	patterns := []string{"*.log", "!important.log", "temp/"}
	matcher, err := dotignore.NewPatternMatcher(patterns)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher: %v", err)
	}

	file := "debug.log"
	matches, err := matcher.Matches(file)
	if err != nil {
		log.Fatalf("Error matching file: %v", err)
	}

	fmt.Printf("%s matches: %v\n", file, matches)

	importantFile := "important.log"
	matches, err = matcher.Matches(importantFile)
	if err != nil {
		log.Fatalf("Error matching file: %v", err)
	}

	fmt.Printf("%s matches: %v\n", importantFile, matches)
}
Output:
debug.log matches: true
important.log matches: false

func NewPatternMatcherFromFile

func NewPatternMatcherFromFile(filePath string) (*PatternMatcher, error)

NewPatternMatcherFromFile reads a file containing ignore patterns and returns a PatternMatcher instance.

Example
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	// Create a temporary file to simulate the test.gitignore file
	fileContent := "*.log\n!important.log\ntemp/"
	fileName := "test.gitignore"
	err := os.WriteFile(fileName, []byte(fileContent), 0644)
	if err != nil {
		log.Fatalf("Failed to create test.gitignore file: %v", err)
	}
	defer os.Remove(fileName) // Ensure the file is cleaned up after the test

	matcher, err := dotignore.NewPatternMatcherFromFile(fileName)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher from file: %v", err)
	}

	file := "debug.log"
	matches, err := matcher.Matches(file)
	if err != nil {
		log.Fatalf("Error matching file: %v", err)
	}

	fmt.Printf("%s matches: %v\n", file, matches)

	importantFile := "important.log"
	matches, err = matcher.Matches(importantFile)
	if err != nil {
		log.Fatalf("Error matching file: %v", err)
	}

	fmt.Printf("%s matches: %v\n", importantFile, matches)
}
Output:
debug.log matches: true
important.log matches: false

func NewPatternMatcherFromReader

func NewPatternMatcherFromReader(reader io.Reader) (*PatternMatcher, error)

NewPatternMatcherFromReader initializes a new PatternMatcher instance from an io.Reader.

Example
package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	reader := strings.NewReader("*.log\n!important.log\ntemp/")
	matcher, err := dotignore.NewPatternMatcherFromReader(reader)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher: %v", err)
	}

	file := "debug.log"
	matches, err := matcher.Matches(file)
	if err != nil {
		log.Fatalf("Error matching file: %v", err)
	}

	fmt.Printf("%s matches: %v\n", file, matches)

	importantFile := "important.log"
	matches, err = matcher.Matches(importantFile)
	if err != nil {
		log.Fatalf("Error matching file: %v", err)
	}

	fmt.Printf("%s matches: %v\n", importantFile, matches)
}
Output:
debug.log matches: true
important.log matches: false

func (*PatternMatcher) Matches

func (p *PatternMatcher) Matches(file string) (bool, error)

Matches checks if the given file path matches any of the ignore patterns in the PatternMatcher. It returns true if the file should be ignored, false otherwise.

Example
package main

import (
	"fmt"
	"log"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	patterns := []string{"*.txt", "reports/"}
	matcher, err := dotignore.NewPatternMatcher(patterns)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher: %v", err)
	}

	files := []string{"notes.txt", "data.json", "reports/summary.pdf", "images/picture.jpg"}

	for _, file := range files {
		matches, err := matcher.Matches(file)
		if err != nil {
			log.Printf("Error matching file %s: %v", file, err)
			continue
		}

		fmt.Printf("%s matches: %v\n", file, matches)
	}
}
Output:
notes.txt matches: true
data.json matches: false
reports/summary.pdf matches: true
images/picture.jpg matches: false
Example (Directories)

ExamplePatternMatcher_Matches_directories demonstrates directory pattern matching

package main

import (
	"fmt"
	"log"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	patterns := []string{"build/", "*.tmp", "logs/**"}
	matcher, err := dotignore.NewPatternMatcher(patterns)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher: %v", err)
	}

	files := []string{
		"build/",               // Directory
		"build/app.js",         // File in ignored directory
		"cache.tmp",            // Temporary file
		"logs/app.log",         // File in logs directory
		"logs/debug/error.log", // Nested file in logs
		"src/main.go",          // Regular source file
	}

	for _, file := range files {
		matches, err := matcher.Matches(file)
		if err != nil {
			log.Printf("Error matching file %s: %v", file, err)
			continue
		}
		fmt.Printf("%-20s matches: %v\n", file, matches)
	}
}
Output:
build/               matches: true
build/app.js         matches: true
cache.tmp            matches: true
logs/app.log         matches: true
logs/debug/error.log matches: true
src/main.go          matches: false
Example (Negation)

ExamplePatternMatcher_Matches_negation demonstrates negation pattern behavior

package main

import (
	"fmt"
	"log"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	patterns := []string{
		"*.log",            // Ignore all log files
		"!important.log",   // But keep important.log
		"build/**",         // Ignore everything in build
		"!build/README.md", // But keep the README
		"!build/docs/**",   // And keep all docs
	}
	matcher, err := dotignore.NewPatternMatcher(patterns)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher: %v", err)
	}

	files := []string{
		"app.log",              // Regular log file
		"important.log",        // Negated log file
		"build/app.js",         // Build artifact
		"build/README.md",      // Negated build file
		"build/docs/api.md",    // Negated docs file
		"build/dist/bundle.js", // Still ignored build file
	}

	for _, file := range files {
		matches, err := matcher.Matches(file)
		if err != nil {
			log.Printf("Error matching file %s: %v", file, err)
			continue
		}
		fmt.Printf("%-22s matches: %v\n", file, matches)
	}
}
Output:
app.log                matches: true
important.log          matches: false
build/app.js           matches: true
build/README.md        matches: false
build/docs/api.md      matches: false
build/dist/bundle.js   matches: true
Example (Wildcards)

ExamplePatternMatcher_Matches_wildcards demonstrates wildcard pattern matching

package main

import (
	"fmt"
	"log"

	"github.com/codeglyph/go-dotignore"
)

func main() {
	patterns := []string{
		"**/*.test.js",   // Test files anywhere
		"src/*/index.js", // Index files in immediate subdirs of src
		"file?.txt",      // Single character wildcard
	}
	matcher, err := dotignore.NewPatternMatcher(patterns)
	if err != nil {
		log.Fatalf("Failed to create pattern matcher: %v", err)
	}

	files := []string{
		"app.test.js",              // Root level test
		"src/utils/helper.test.js", // Nested test file
		"src/components/index.js",  // Index in component dir
		"src/utils/other.js",       // Non-index file in utils
		"file1.txt",                // Single char wildcard match
		"file10.txt",               // Multiple chars - no match
	}

	for _, file := range files {
		matches, err := matcher.Matches(file)
		if err != nil {
			log.Printf("Error matching file %s: %v", file, err)
			continue
		}
		fmt.Printf("%-25s matches: %v\n", file, matches)
	}
}
Output:
app.test.js               matches: true
src/utils/helper.test.js  matches: true
src/components/index.js   matches: true
src/utils/other.js        matches: false
file1.txt                 matches: true
file10.txt                matches: false

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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