From 8487a598b42192c351c0d6b731068cc69ac6169f Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Thu, 2 Apr 2015 23:07:05 -0600 Subject: [PATCH] matches for pull + list in --- README.md | 13 +++++++++++++ cmd/drive/main.go | 44 +++++++++++++++++++++++++++++++++++++++----- src/changes.go | 39 +++++++++++++++++++++++++++++++++------ src/list.go | 35 +++++++++++++++++++++++++++++++++-- src/pull.go | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0681afb6..81b3cf72 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,13 @@ Run it without any arguments to pull all of the files from the current path: $ drive pull ``` +Pulling by matches is also supported + +```shell +$ cd ~/myDrive/content/2015 +$ drive pull --matches vines docx +``` + To force download from paths that otherwise would be marked with no-changes ```shell @@ -318,6 +325,12 @@ Pass in a directory path to list files in that directory: $ drive list photos ``` +To list matches + +```shell +$ drive list --matches mp4 go +``` + The `-trashed` option can be specified to show trashed files in the listing: ```shell diff --git a/cmd/drive/main.go b/cmd/drive/main.go index 4f097ac4..95691181 100644 --- a/cmd/drive/main.go +++ b/cmd/drive/main.go @@ -141,6 +141,7 @@ type listCmd struct { shared *bool inTrash *bool version *bool + matches *bool owners *bool quiet *bool } @@ -158,13 +159,25 @@ func (cmd *listCmd) Flags(fs *flag.FlagSet) *flag.FlagSet { cmd.noPrompt = fs.Bool("no-prompt", false, "shows no prompt before pagination") cmd.owners = fs.Bool("owners", false, "shows the owner names per file") cmd.recursive = fs.Bool("r", false, "recursively list subdirectories") + cmd.matches = fs.Bool("matches", false, "list by prefix") cmd.quiet = fs.Bool(drive.QuietKey, false, "if set, do not log anything but errors") return fs } func (cmd *listCmd) Run(args []string) { - sources, context, path := preprocessArgs(args) + var path string + var sources []string + var context *config.Context + + if !*cmd.matches { + sources, context, path = preprocessArgs(args) + } else { + cwd, err := os.Getwd() + exitWithError(err) + sources = args + _, context, path = preprocessArgs([]string{cwd}) + } typeMask := 0 if *cmd.directories { @@ -189,7 +202,7 @@ func (cmd *listCmd) Run(args []string) { typeMask |= drive.Minimal } - exitWithError(drive.New(context, &drive.Options{ + options := drive.Options{ Depth: *cmd.depth, Hidden: *cmd.hidden, InTrash: *cmd.inTrash, @@ -200,7 +213,13 @@ func (cmd *listCmd) Run(args []string) { Sources: sources, TypeMask: typeMask, Quiet: *cmd.quiet, - }).List()) + } + + if *cmd.matches { + exitWithError(drive.New(context, &options).ListMatches()) + } else { + exitWithError(drive.New(context, &options).List()) + } } type statCmd struct { @@ -233,6 +252,7 @@ type pullCmd struct { export *string force *bool hidden *bool + matches *bool noPrompt *bool noClobber *bool recursive *bool @@ -253,6 +273,7 @@ func (cmd *pullCmd) Flags(fs *flag.FlagSet) *flag.FlagSet { cmd.ignoreChecksum = fs.Bool(drive.CLIOptionIgnoreChecksum, false, drive.DescIgnoreChecksum) cmd.ignoreConflict = fs.Bool(drive.CLIOptionIgnoreConflict, false, drive.DescIgnoreConflict) cmd.exportsDir = fs.String("export-dir", "", "directory to place exports") + cmd.matches = fs.Bool("matches", false, "search by prefix") cmd.piped = fs.Bool("piped", false, "if true, read content from stdin") cmd.quiet = fs.Bool(drive.QuietKey, false, "if set, do not log anything but errors") @@ -260,7 +281,18 @@ func (cmd *pullCmd) Flags(fs *flag.FlagSet) *flag.FlagSet { } func (cmd *pullCmd) Run(args []string) { - sources, context, path := preprocessArgs(args) + var path string + var sources []string + var context *config.Context + + if !*cmd.matches { + sources, context, path = preprocessArgs(args) + } else { + cwd, err := os.Getwd() + exitWithError(err) + sources = args + _, context, path = preprocessArgs([]string{cwd}) + } // Filter out empty strings. exports := drive.NonEmptyStrings(strings.Split(*cmd.export, ",")) @@ -281,7 +313,9 @@ func (cmd *pullCmd) Run(args []string) { Quiet: *cmd.quiet, } - if *cmd.piped { + if *cmd.matches { + exitWithError(drive.New(context, options).PullMatches()) + } else if *cmd.piped { exitWithError(drive.New(context, options).PullPiped()) } else { exitWithError(drive.New(context, options).Pull()) diff --git a/src/changes.go b/src/changes.go index 60bb33ab..dda951f6 100644 --- a/src/changes.go +++ b/src/changes.go @@ -83,6 +83,31 @@ func (g *Commands) pathResolve() (relPath, absPath string, err error) { return } +func (g *Commands) resolveToLocalFile(relToRoot, fsPath string) (local *File, err error) { + if g.opts.IgnoreRegexp != nil && g.opts.IgnoreRegexp.Match([]byte(relToRoot)) { + err = fmt.Errorf("\n'%s' is set to be ignored yet is being processed. Use `%s` to override this\n", relToRoot, ForceKey) + return + } + + localinfo, _ := os.Stat(fsPath) + if localinfo != nil { + local = NewLocalFile(fsPath, localinfo) + } + + return +} + +func (g *Commands) byRemoteResolve(relToRoot, fsPath string, r *File, isPush bool) (cl []*Change, err error) { + var l *File + l, err = g.resolveToLocalFile(relToRoot, fsPath) + if err != nil { + g.log.LogErrf("%v\n", err) + return cl, nil + } + + return g.doChangeListRecv(relToRoot, fsPath, l, r, isPush) +} + func (g *Commands) changeListResolve(relToRoot, fsPath string, isPush bool) (cl []*Change, err error) { var r, l *File r, err = g.rem.FindByPath(relToRoot) @@ -93,17 +118,19 @@ func (g *Commands) changeListResolve(relToRoot, fsPath string, isPush bool) (cl } } - if g.opts.IgnoreRegexp != nil && g.opts.IgnoreRegexp.Match([]byte(relToRoot)) { - g.log.LogErrf("\n'%s' is set to be ignored yet is being processed. Use `%s` to override this\n", relToRoot, ForceKey) + l, err = g.resolveToLocalFile(relToRoot, fsPath) + if err != nil { + g.log.LogErrf("%s: %v\n", relToRoot, err) return cl, nil } - localinfo, _ := os.Stat(fsPath) - if localinfo != nil { - l = NewLocalFile(fsPath, localinfo) - } + return g.doChangeListRecv(relToRoot, fsPath, l, r, isPush) +} +func (g *Commands) doChangeListRecv(relToRoot, fsPath string, l, r *File, isPush bool) (cl []*Change, err error) { if l == nil && r == nil { + err = fmt.Errorf("'%s' aka '%s' doesn't exist locally nor remotely", + relToRoot, fsPath) err = fmt.Errorf("'%s' aka '%s' doesn't exist locally nor remotely", relToRoot, fsPath) return diff --git a/src/list.go b/src/list.go index fe39d384..b73eb04c 100644 --- a/src/list.go +++ b/src/list.go @@ -45,8 +45,39 @@ type traversalSt struct { inTrash bool } -func (g *Commands) List() (err error) { +func (g *Commands) ListMatches() (error) { + matches, err := g.rem.FindMatches(g.opts.Path, g.opts.Sources, g.opts.InTrash) + if err != nil { + return err + } + + spin := g.playabler() + spin.play() + + for match := range matches { + if match == nil { + continue + } + + travSt := traversalSt{ + depth: g.opts.Depth, + file: match, + headPath: g.opts.Path, + inTrash: g.opts.InTrash, + mask: g.opts.TypeMask, + } + + if !g.breadthFirst(travSt, spin) { + break + } + } + spin.stop() + + return nil +} + +func (g *Commands) List() (err error) { resolver := g.rem.FindByPath if g.opts.InTrash { resolver = g.rem.FindByPathTrashed @@ -92,7 +123,7 @@ func (g *Commands) List() (err error) { depth: g.opts.Depth, file: kv.value.(*File), headPath: kv.key, - inTrash: false, + inTrash: g.opts.InTrash, mask: g.opts.TypeMask, } diff --git a/src/pull.go b/src/pull.go index cf093919..218b03af 100644 --- a/src/pull.go +++ b/src/pull.go @@ -75,6 +75,45 @@ func (g *Commands) Pull() (err error) { return g.playPullChangeList(nonConflicts, g.opts.Exports) } +func (g *Commands) PullMatches() (err error) { + var cl []*Change + matches, err := g.rem.FindMatches(g.opts.Path, g.opts.Sources, false) + + if err != nil { + return err + } + + p := g.opts.Path + if p == "/" { + p = "" + } + + for match := range matches { + if match == nil { + continue + } + relToRoot := "/" + match.Name + fsPath := g.context.AbsPathOf(relToRoot) + + ccl, cErr := g.byRemoteResolve(relToRoot, fsPath, match, false) + if cErr != nil { + continue + } + + cl = append(cl, ccl...) + } + + if len(cl) < 1 { + return fmt.Errorf("no matches found!") + } + + ok := printChangeList(g.log, cl, !g.opts.canPrompt(), g.opts.NoClobber) + if ok { + return g.playPullChangeList(cl, g.opts.Exports) + } + return nil +} + func (g *Commands) PullPiped() (err error) { // Cannot pull asynchronously because the pull order must be maintained for _, relToRootPath := range g.opts.Sources {