Skip to content

Commit

Permalink
feat: Make unmanaged command accept destination directory args
Browse files Browse the repository at this point in the history
  • Loading branch information
RuijieYu authored and twpayne committed Jul 27, 2022
1 parent 9316c5a commit f8eabef
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 12 deletions.
8 changes: 6 additions & 2 deletions assets/chezmoi.io/docs/reference/commands/unmanaged.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# `unmanaged`
# `unmanaged` [*target*...]

List all unmanaged files in the destination directory.
List all unmanaged files in *target*s. When no *target*s are supplied, list all
unmanaged files in the destination directory.

It is an error to supply *target*s that are not found on the filesystem.

!!! example

```console
$ chezmoi unmanaged
$ chezmoi unmanaged ~/.config/chezmoi ~/.ssh
```
22 changes: 22 additions & 0 deletions pkg/cmd/testdata/scripts/unmanaged.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ rm $CHEZMOISOURCEDIR/dot_file
chezmoi unmanaged
cmp stdout golden/unmanaged-dir-file

# test chezmoi unmanaged with arguments
chezmoi unmanaged $HOME${/}.dir $HOME${/}.file
cmp stdout golden/unmanaged-with-args

# test chezmoi unmanaged, with child of unmanaged dir as argument
chezmoi unmanaged $HOME${/}.dir/subdir
cmp stdout golden/unmanaged-inside-unmanaged

# test chezmoi unmanaged with managed arguments
chezmoi unmanaged $HOME${/}.create $HOME${/}.file
cmp stdout golden/unmanaged-with-some-managed

# test that chezmoi unmanaged with absent paths should fail
! chezmoi unmanaged $HOME${/}absent-path

-- golden/unmanaged --
.local
-- golden/unmanaged-dir --
Expand All @@ -21,3 +36,10 @@ cmp stdout golden/unmanaged-dir-file
.dir
.file
.local
-- golden/unmanaged-with-args --
.dir
.file
-- golden/unmanaged-inside-unmanaged --
.dir/subdir
-- golden/unmanaged-with-some-managed --
.file
61 changes: 51 additions & 10 deletions pkg/cmd/unmanagedcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"io/fs"
"sort"
"strings"

"github.com/spf13/cobra"
Expand All @@ -12,40 +13,80 @@ import (

func (c *Config) newUnmanagedCmd() *cobra.Command {
unmanagedCmd := &cobra.Command{
Use: "unmanaged",
Use: "unmanaged [paths]...",
Short: "List the unmanaged files in the destination directory",
Long: mustLongHelp("unmanaged"),
Example: example("unmanaged"),
Args: cobra.NoArgs,
Args: cobra.ArbitraryArgs,
RunE: c.makeRunEWithSourceState(c.runUnmanagedCmd),
}

return unmanagedCmd
}

func (c *Config) runUnmanagedCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error {
builder := strings.Builder{}
// the set of discovered, unmanaged items
unmanaged := map[string]bool{}
walkFunc := func(destAbsPath chezmoi.AbsPath, fileInfo fs.FileInfo, err error) error {
if err != nil {
return err
}
if destAbsPath == c.DestDirAbsPath {
return nil
}
targeRelPath := destAbsPath.MustTrimDirPrefix(c.DestDirAbsPath)
managed := sourceState.Contains(targeRelPath)
ignored := sourceState.Ignore(targeRelPath)
targetRelPath, err := destAbsPath.TrimDirPrefix(c.DestDirAbsPath)
if err != nil {
return err
}
managed := sourceState.Contains(targetRelPath)
ignored := sourceState.Ignore(targetRelPath)
if !managed && !ignored {
builder.WriteString(targeRelPath.String())
builder.WriteByte('\n')
unmanaged[targetRelPath.String()] = true
}
if fileInfo.IsDir() && (!managed || ignored) {
return vfs.SkipDir
}
return nil
}
if err := chezmoi.Walk(c.destSystem, c.DestDirAbsPath, walkFunc); err != nil {
return err

// Build queued paths. When no arguments, start from root; otherwise start
// from arguments. The paths are deduplicated and sorted.
paths := make([]chezmoi.AbsPath, 0, len(args)) // (lsttype, size, capacity)
if len(args) == 0 {
paths = append(paths, c.DestDirAbsPath)
} else {
qPaths := make(map[chezmoi.AbsPath]bool, len(args)) // (map, capacity)
for _, arg := range args {
p, err := chezmoi.NormalizePath(arg)
if err != nil {
return err
}
qPaths[p] = true
}
for path := range qPaths {
paths = append(paths, path)
}
sort.Slice(paths,
func(i, j int) bool { return paths[i].Less(paths[j]) })
}

for _, path := range paths {
if err := chezmoi.Walk(c.destSystem, path, walkFunc); err != nil {
return err
}
}

// collect the keys and sort
builder := strings.Builder{}
unmPaths := make([]string, 0, len(unmanaged))
for path := range unmanaged {
unmPaths = append(unmPaths, path)
}
sort.Strings(unmPaths)

for _, path := range unmPaths {
builder.WriteString(path)
builder.WriteByte('\n')
}
return c.writeOutputString(builder.String())
}

0 comments on commit f8eabef

Please sign in to comment.