Skip to content

Commit

Permalink
Add undocumented --safe=false command line flag
Browse files Browse the repository at this point in the history
In testing, this speeds up chezmoi by about 30% which is irrelevant when
the runtime is a few seconds.
  • Loading branch information
twpayne committed Sep 1, 2021
1 parent 570f91a commit 091979d
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 8 deletions.
3 changes: 3 additions & 0 deletions internal/chezmoi/realsystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import (
"github.com/twpayne/chezmoi/v2/internal/chezmoilog"
)

// A RealSystemOption sets an option on a RealSystem.
type RealSystemOption func(*RealSystem)

// Glob implements System.Glob.
func (s *RealSystem) Glob(pattern string) ([]string, error) {
return doublestar.Glob(s.UnderlyingFS(), pattern)
Expand Down
25 changes: 19 additions & 6 deletions internal/chezmoi/realsystem_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,30 @@ import (
// An RealSystem is a System that writes to a filesystem and executes scripts.
type RealSystem struct {
fileSystem vfs.FS
safe bool
devCache map[AbsPath]uint // devCache maps directories to device numbers.
tempDirCache map[uint]string // tempDirCache maps device numbers to renameio temporary directories.
}

// RealSystemWithSafe sets the safe flag of the RealSystem.
func RealSystemWithSafe(safe bool) RealSystemOption {
return func(s *RealSystem) {
s.safe = safe
}
}

// NewRealSystem returns a System that acts on fileSystem.
func NewRealSystem(fileSystem vfs.FS) *RealSystem {
return &RealSystem{
func NewRealSystem(fileSystem vfs.FS, options ...RealSystemOption) *RealSystem {
s := &RealSystem{
fileSystem: fileSystem,
safe: true,
devCache: make(map[AbsPath]uint),
tempDirCache: make(map[uint]string),
}
for _, option := range options {
option(s)
}
return s
}

// Chmod implements System.Chmod.
Expand All @@ -42,9 +55,9 @@ func (s *RealSystem) Readlink(name AbsPath) (string, error) {

// WriteFile implements System.WriteFile.
func (s *RealSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error {
// Special case: if writing to the real filesystem, use
// Special case: if writing to the real filesystem in safe mode, use
// github.com/google/renameio.
if s.fileSystem == vfs.OSFS {
if s.safe && s.fileSystem == vfs.OSFS {
dir := filename.Dir()
dev, ok := s.devCache[dir]
if !ok {
Expand Down Expand Up @@ -85,9 +98,9 @@ func (s *RealSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode)

// WriteSymlink implements System.WriteSymlink.
func (s *RealSystem) WriteSymlink(oldname string, newname AbsPath) error {
// Special case: if writing to the real filesystem, use
// Special case: if writing to the real filesystem in safe mode, use
// github.com/google/renameio.
if s.fileSystem == vfs.OSFS {
if s.safe && s.fileSystem == vfs.OSFS {
return renameio.Symlink(oldname, string(newname))
}
if err := s.fileSystem.RemoveAll(string(newname)); err != nil && !errors.Is(err, fs.ErrNotExist) {
Expand Down
10 changes: 9 additions & 1 deletion internal/chezmoi/realsystem_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ type RealSystem struct {
fileSystem vfs.FS
}

// RealSystemWithSafe sets the safe flag of the RealSystem. On Windows it does
// nothing as Windows does not support atomic file or symlink updates. See
// https://github.com/google/renameio/issues/1 and
// https://github.com/golang/go/issues/22397#issuecomment-498856679.
func RealSystemWithSafe(safe bool) RealSystemOption {
return func(s *RealSystem) {}
}

// NewRealSystem returns a System that acts on fs.
func NewRealSystem(fileSystem vfs.FS) *RealSystem {
func NewRealSystem(fileSystem vfs.FS, options ...RealSystemOption) *RealSystem {
return &RealSystem{
fileSystem: fileSystem,
}
Expand Down
8 changes: 7 additions & 1 deletion internal/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type Config struct {
Interpreters map[string]*chezmoi.Interpreter `mapstructure:"interpreters"`
Mode chezmoi.Mode `mapstructure:"mode"`
Pager string `mapstructure:"pager"`
Safe bool `mapstructure:"safe"`
Remove bool `mapstructure:"remove"`
SourceDirAbsPath chezmoi.AbsPath `mapstructure:"sourceDir"`
Template templateConfig `mapstructure:"template"`
Expand Down Expand Up @@ -216,6 +217,7 @@ func newConfig(options ...configOption) (*Config, error) {
},
Interpreters: defaultInterpreters,
Pager: os.Getenv("PAGER"),
Safe: true,
Template: templateConfig{
Options: chezmoi.DefaultTemplateOptions,
},
Expand Down Expand Up @@ -1023,6 +1025,7 @@ func (c *Config) newRootCmd() (*cobra.Command, error) {

persistentFlags.Var(&c.Color, "color", "Colorize output")
persistentFlags.VarP(&c.DestDirAbsPath, "destination", "D", "Set destination directory")
persistentFlags.BoolVar(&c.Safe, "safe", c.Safe, "Safely replace files and symlinks")
persistentFlags.BoolVar(&c.Remove, "remove", c.Remove, "Remove entries from destination directory")
persistentFlags.VarP(&c.SourceDirAbsPath, "source", "S", "Set source directory")
persistentFlags.Var(&c.Mode, "mode", "Mode")
Expand Down Expand Up @@ -1061,6 +1064,7 @@ func (c *Config) newRootCmd() (*cobra.Command, error) {
rootCmd.MarkPersistentFlagDirname("destination"),
persistentFlags.MarkHidden("gops"),
rootCmd.MarkPersistentFlagFilename("output"),
persistentFlags.MarkHidden("safe"),
rootCmd.MarkPersistentFlagDirname("source"),
} {
if err != nil {
Expand Down Expand Up @@ -1260,7 +1264,9 @@ func (c *Config) persistentPreRunRootE(cmd *cobra.Command, args []string) error
zerolog.SetGlobalLevel(zerolog.Disabled)
}

c.baseSystem = chezmoi.NewRealSystem(c.fileSystem)
c.baseSystem = chezmoi.NewRealSystem(c.fileSystem,
chezmoi.RealSystemWithSafe(c.Safe),
)
if c.debug {
c.baseSystem = chezmoi.NewDebugSystem(c.baseSystem)
}
Expand Down

0 comments on commit 091979d

Please sign in to comment.