Skip to content

Commit

Permalink
Add .chezmoiignore support, fixes #91
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Jan 12, 2019
1 parent cab2f01 commit d66bf04
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 54 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ will be removed. This can be used to ensure that files are only present on
certain machines. If you want an empty file to be created anyway, you will need
to give it an `empty_` prefix. See "Under the hood" below.

For coarser-grained control of files and entire directories are managed on
different machines, or to exclude certain files completely, you can create
`.chezmoiignore` files in the source directory. These specify a list of
patterns that `chezmoi` should ignore, and are interpreted as templates. An
example `.chezmoiignore` file might look like:

README.md
{{- if ne .chezmoi.hostname "work-laptop" }}
.work # only manage .work on work-laptop
{{- end }}


## Keeping data private

Expand Down
2 changes: 1 addition & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c *Config) applyArgs(fs vfs.FS, args []string, mutator chezmoi.Mutator) er
return err
}
for _, entry := range entries {
if err := entry.Apply(fs, ts.TargetDir, ts.Umask, mutator); err != nil {
if err := entry.Apply(fs, ts.TargetDir, ts.TargetIgnore.Match, ts.Umask, mutator); err != nil {
return err
}
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (c *Config) runEditCommand(fs vfs.FS, command *cobra.Command, args []string
if c.edit.diff {
mutator = chezmoi.NewLoggingMutator(os.Stdout, mutator)
}
if err := entry.Apply(readOnlyFS, ts.TargetDir, ts.Umask, mutator); err != nil {
if err := entry.Apply(readOnlyFS, ts.TargetDir, ts.TargetIgnore.Match, ts.Umask, mutator); err != nil {
return err
}
if c.edit.apply && anyMutator.Mutated() {
Expand All @@ -81,7 +81,7 @@ func (c *Config) runEditCommand(fs vfs.FS, command *cobra.Command, args []string
c.edit.prompt = false
}
}
if err := entry.Apply(readOnlyFS, ts.TargetDir, ts.Umask, applyMutator); err != nil {
if err := entry.Apply(readOnlyFS, ts.TargetDir, ts.TargetIgnore.Match, ts.Umask, applyMutator); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/chezmoi/chezmoi.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type templateFuncError struct {

// An Entry is either a Dir, a File, or a Symlink.
type Entry interface {
Apply(fs vfs.FS, targetDir string, umask os.FileMode, mutator Mutator) error
Apply(fs vfs.FS, targetDir string, ignore func(string) bool, umask os.FileMode, mutator Mutator) error
ConcreteValue(targetDir, sourceDir string, recursive bool) (interface{}, error)
Evaluate() error
SourceName() string
Expand Down
7 changes: 5 additions & 2 deletions lib/chezmoi/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ func newDir(sourceName string, targetName string, exact bool, perm os.FileMode)
}

// Apply ensures that targetDir in fs matches d.
func (d *Dir) Apply(fs vfs.FS, targetDir string, umask os.FileMode, mutator Mutator) error {
func (d *Dir) Apply(fs vfs.FS, targetDir string, ignore func(string) bool, umask os.FileMode, mutator Mutator) error {
if ignore(d.targetName) {
return nil
}
targetPath := filepath.Join(targetDir, d.targetName)
info, err := fs.Lstat(targetPath)
switch {
Expand All @@ -109,7 +112,7 @@ func (d *Dir) Apply(fs vfs.FS, targetDir string, umask os.FileMode, mutator Muta
return err
}
for _, entryName := range sortedEntryNames(d.Entries) {
if err := d.Entries[entryName].Apply(fs, targetDir, umask, mutator); err != nil {
if err := d.Entries[entryName].Apply(fs, targetDir, ignore, umask, mutator); err != nil {
return err
}
}
Expand Down
5 changes: 4 additions & 1 deletion lib/chezmoi/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ func (fa FileAttributes) SourceName() string {
}

// Apply ensures that the state of targetPath in fs matches f.
func (f *File) Apply(fs vfs.FS, targetDir string, umask os.FileMode, mutator Mutator) error {
func (f *File) Apply(fs vfs.FS, targetDir string, ignore func(string) bool, umask os.FileMode, mutator Mutator) error {
if ignore(f.targetName) {
return nil
}
contents, err := f.Contents()
if err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion lib/chezmoi/symlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ type symlinkConcreteValue struct {
}

// Apply ensures that the state of s's target in fs matches s.
func (s *Symlink) Apply(fs vfs.FS, targetDir string, umask os.FileMode, mutator Mutator) error {
func (s *Symlink) Apply(fs vfs.FS, targetDir string, ignore func(string) bool, umask os.FileMode, mutator Mutator) error {
if ignore(s.targetName) {
return nil
}
target, err := s.Linkname()
if err != nil {
return err
Expand Down
62 changes: 48 additions & 14 deletions lib/chezmoi/target_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package chezmoi

import (
"archive/tar"
"bufio"
"bytes"
"fmt"
"io"
Expand Down Expand Up @@ -33,23 +34,25 @@ type ImportTAROptions struct {

// A TargetState represents the root target state.
type TargetState struct {
TargetDir string
Umask os.FileMode
SourceDir string
Data map[string]interface{}
Funcs template.FuncMap
Entries map[string]Entry
TargetDir string
TargetIgnore PatternSet
Umask os.FileMode
SourceDir string
Data map[string]interface{}
Funcs template.FuncMap
Entries map[string]Entry
}

// NewTargetState creates a new TargetState.
func NewTargetState(targetDir string, umask os.FileMode, sourceDir string, data map[string]interface{}, funcs template.FuncMap) *TargetState {
return &TargetState{
TargetDir: targetDir,
Umask: umask,
SourceDir: sourceDir,
Data: data,
Funcs: funcs,
Entries: make(map[string]Entry),
TargetDir: targetDir,
TargetIgnore: NewPatternSet(),
Umask: umask,
SourceDir: sourceDir,
Data: data,
Funcs: funcs,
Entries: make(map[string]Entry),
}
}

Expand Down Expand Up @@ -168,7 +171,7 @@ func (ts *TargetState) Archive(w *tar.Writer, umask os.FileMode) error {
// Apply ensures that ts.TargetDir in fs matches ts.
func (ts *TargetState) Apply(fs vfs.FS, mutator Mutator) error {
for _, entryName := range sortedEntryNames(ts.Entries) {
if err := ts.Entries[entryName].Apply(fs, ts.TargetDir, ts.Umask, mutator); err != nil {
if err := ts.Entries[entryName].Apply(fs, ts.TargetDir, ts.TargetIgnore.Match, ts.Umask, mutator); err != nil {
return err
}
}
Expand Down Expand Up @@ -242,8 +245,12 @@ func (ts *TargetState) Populate(fs vfs.FS) error {
if relPath == "." {
return nil
}
// Ignore all files and directories beginning with "."
// Treat all files and directories beginning with "." specially.
if _, name := filepath.Split(relPath); strings.HasPrefix(name, ".") {
if info.Name() == ".chezmoiignore" {
return ts.addSourceIgnore(fs, path, relPath)
}
// Ignore all other files and directories.
if info.IsDir() {
return filepath.SkipDir
}
Expand Down Expand Up @@ -546,3 +553,30 @@ func (ts *TargetState) importHeader(r io.Reader, importTAROptions ImportTAROptio
return fmt.Errorf("%s: unspported typeflag '%c'", header.Name, header.Typeflag)
}
}

func (ts *TargetState) addSourceIgnore(fs vfs.FS, path, relPath string) error {
data, err := ts.executeTemplate(fs, path)
if err != nil {
return err
}
dir := filepath.Dir(relPath)
s := bufio.NewScanner(bytes.NewReader(data))
for s.Scan() {
text := s.Text()
if index := strings.IndexRune(text, '#'); index != -1 {
text = text[:index]
}
text = strings.TrimSpace(text)
if text == "" {
continue
}
pattern := filepath.Join(dir, text)
if err := ts.TargetIgnore.Add(pattern); err != nil {
return fmt.Errorf("%s: %v", path, err)
}
}
if err := s.Err(); err != nil {
return fmt.Errorf("%s: %v", path, err)
}
return nil
}
Loading

0 comments on commit d66bf04

Please sign in to comment.