diff options
| -rw-r--r-- | compare.go | 27 | ||||
| -rw-r--r-- | drive/sync.go | 114 | ||||
| -rw-r--r-- | drive/sync_download.go | 3 | ||||
| -rw-r--r-- | drive/sync_upload.go | 11 | ||||
| -rw-r--r-- | drive/util.go | 14 | ||||
| -rw-r--r-- | handlers_drive.go | 2 | 
6 files changed, 110 insertions, 61 deletions
| diff --git a/compare.go b/compare.go new file mode 100644 index 0000000..7275a38 --- /dev/null +++ b/compare.go @@ -0,0 +1,27 @@ +package main + +import ( +	"fmt" +    "os" +    "io" +    "crypto/md5" +	"./drive" +) + +type Md5Comparer struct {} + +func (self Md5Comparer) Changed(local *drive.LocalFile, remote *drive.RemoteFile) bool { +    return remote.Md5() != md5sum(local.AbsPath()) +} + +func md5sum(path string) string { +    h := md5.New() +    f, err := os.Open(path) +    if err != nil { +        return "" +    } +    defer f.Close() + +    io.Copy(h, f) +    return fmt.Sprintf("%x", h.Sum(nil)) +} diff --git a/drive/sync.go b/drive/sync.go index c8396c1..cfd5acb 100644 --- a/drive/sync.go +++ b/drive/sync.go @@ -1,6 +1,7 @@  package drive  import ( +    "time"      "fmt"      "os"      "path/filepath" @@ -13,18 +14,18 @@ import (  const DefaultIgnoreFile = ".gdriveignore" -func (self *Drive) prepareSyncFiles(localPath string, root *drive.File) (*syncFiles, error) { -    localCh := make(chan struct{files []*localFile; err error}) -    remoteCh := make(chan struct{files []*remoteFile; err error}) +func (self *Drive) prepareSyncFiles(localPath string, root *drive.File, cmp FileComparer) (*syncFiles, error) { +    localCh := make(chan struct{files []*LocalFile; err error}) +    remoteCh := make(chan struct{files []*RemoteFile; err error})      go func() {          files, err := prepareLocalFiles(localPath) -        localCh <- struct{files []*localFile; err error}{files, err} +        localCh <- struct{files []*LocalFile; err error}{files, err}      }()      go func() {          files, err := self.prepareRemoteFiles(root) -        remoteCh <- struct{files []*remoteFile; err error}{files, err} +        remoteCh <- struct{files []*RemoteFile; err error}{files, err}      }()      local := <-localCh @@ -38,14 +39,15 @@ func (self *Drive) prepareSyncFiles(localPath string, root *drive.File) (*syncFi      }      return &syncFiles{ -        root: &remoteFile{file: root}, +        root: &RemoteFile{file: root},          local: local.files,          remote: remote.files, +        compare: cmp,      }, nil  } -func prepareLocalFiles(root string) ([]*localFile, error) { -    var files []*localFile +func prepareLocalFiles(root string) ([]*LocalFile, error) { +    var files []*LocalFile      // Get absolute root path      absRootPath, err := filepath.Abs(root) @@ -85,7 +87,7 @@ func prepareLocalFiles(root string) ([]*localFile, error) {              return nil          } -        files = append(files, &localFile{ +        files = append(files, &LocalFile{              absPath: absPath,              relPath: relPath,              info: info, @@ -112,7 +114,7 @@ func (self *Drive) listAllFiles(q string, fields []googleapi.Field) ([]*drive.Fi      return files, err  } -func (self *Drive) prepareRemoteFiles(rootDir *drive.File) ([]*remoteFile, error) { +func (self *Drive) prepareRemoteFiles(rootDir *drive.File) ([]*RemoteFile, error) {      // Find all files which has rootDir as root      query := fmt.Sprintf("appProperties has {key='syncRootId' and value='%s'}", rootDir.Id)      fields := []googleapi.Field{"nextPageToken", "files(id,name,parents,md5Checksum,mimeType)"} @@ -130,13 +132,13 @@ func (self *Drive) prepareRemoteFiles(rootDir *drive.File) ([]*remoteFile, error          return nil, err      } -    var remoteFiles []*remoteFile +    var remoteFiles []*RemoteFile      for _, f := range files {          relPath, ok := relPaths[f.Id]          if !ok {              return nil, fmt.Errorf("File %s does not have a valid parent", f.Id)          } -        remoteFiles = append(remoteFiles, &remoteFile{ +        remoteFiles = append(remoteFiles, &RemoteFile{              relPath: relPath,              file: f,          }) @@ -230,30 +232,60 @@ func checkFiles(files []*drive.File) error {      return nil  } -type localFile struct { +type LocalFile struct {      absPath string      relPath string      info os.FileInfo  } -type remoteFile struct { +type RemoteFile struct {      relPath string      file *drive.File  }  type changedFile struct { -    local *localFile -    remote *remoteFile +    local *LocalFile +    remote *RemoteFile  }  type syncFiles struct { -    root *remoteFile -    local []*localFile -    remote []*remoteFile +    root *RemoteFile +    local []*LocalFile +    remote []*RemoteFile +    compare FileComparer  } -func (self *syncFiles) filterMissingRemoteDirs() []*localFile { -    var files []*localFile +type FileComparer interface { +    Changed(*LocalFile, *RemoteFile) bool +} + +func (self LocalFile) AbsPath() string { +    return self.absPath +} + +func (self LocalFile) Size() int64 { +    return self.info.Size() +} + +func (self LocalFile) Modified() time.Time { +    return self.info.ModTime() +} + +func (self RemoteFile) Md5() string { +    return self.file.Md5Checksum +} + +func (self RemoteFile) Size() int64 { +    return self.file.Size +} + +func (self RemoteFile) Modified() time.Time { +    t, _ := time.Parse(time.RFC3339, self.file.ModifiedTime) +    return t +} + +func (self *syncFiles) filterMissingRemoteDirs() []*LocalFile { +    var files []*LocalFile      for _, lf := range self.local {          if lf.info.IsDir() && !self.existsRemote(lf) { @@ -264,8 +296,8 @@ func (self *syncFiles) filterMissingRemoteDirs() []*localFile {      return files  } -func (self *syncFiles) filterMissingLocalDirs() []*remoteFile { -    var files []*remoteFile +func (self *syncFiles) filterMissingLocalDirs() []*RemoteFile { +    var files []*RemoteFile      for _, rf := range self.remote {          if isDir(rf.file) && !self.existsLocal(rf) { @@ -276,8 +308,8 @@ func (self *syncFiles) filterMissingLocalDirs() []*remoteFile {      return files  } -func (self *syncFiles) filterMissingRemoteFiles() []*localFile { -    var files []*localFile +func (self *syncFiles) filterMissingRemoteFiles() []*LocalFile { +    var files []*LocalFile      for _, lf := range self.local {          if !lf.info.IsDir() && !self.existsRemote(lf) { @@ -288,8 +320,8 @@ func (self *syncFiles) filterMissingRemoteFiles() []*localFile {      return files  } -func (self *syncFiles) filterMissingLocalFiles() []*remoteFile { -    var files []*remoteFile +func (self *syncFiles) filterMissingLocalFiles() []*RemoteFile { +    var files []*RemoteFile      for _, rf := range self.remote {          if !isDir(rf.file) && !self.existsLocal(rf) { @@ -315,8 +347,8 @@ func (self *syncFiles) filterChangedLocalFiles() []*changedFile {              continue          } -        // Add files where remote md5 sum does not match local -        if rf.file.Md5Checksum != md5sum(lf.absPath) { +        // Check if file has changed +        if self.compare.Changed(lf, rf) {              files = append(files, &changedFile{                  local: lf,                  remote: rf, @@ -342,8 +374,8 @@ func (self *syncFiles) filterChangedRemoteFiles() []*changedFile {              continue          } -        // Add files where remote md5 sum does not match local -        if rf.file.Md5Checksum != md5sum(lf.absPath) { +        // Check if file has changed +        if self.compare.Changed(lf, rf) {              files = append(files, &changedFile{                  local: lf,                  remote: rf, @@ -354,8 +386,8 @@ func (self *syncFiles) filterChangedRemoteFiles() []*changedFile {      return files  } -func (self *syncFiles) filterExtraneousRemoteFiles() []*remoteFile { -    var files []*remoteFile +func (self *syncFiles) filterExtraneousRemoteFiles() []*RemoteFile { +    var files []*RemoteFile      for _, rf := range self.remote {          if !self.existsLocal(rf) { @@ -366,8 +398,8 @@ func (self *syncFiles) filterExtraneousRemoteFiles() []*remoteFile {      return files  } -func (self *syncFiles) filterExtraneousLocalFiles() []*localFile { -    var files []*localFile +func (self *syncFiles) filterExtraneousLocalFiles() []*LocalFile { +    var files []*LocalFile      for _, lf := range self.local {          if !self.existsRemote(lf) { @@ -378,17 +410,17 @@ func (self *syncFiles) filterExtraneousLocalFiles() []*localFile {      return files  } -func (self *syncFiles) existsRemote(lf *localFile) bool { +func (self *syncFiles) existsRemote(lf *LocalFile) bool {      _, found := self.findRemoteByPath(lf.relPath)      return found  } -func (self *syncFiles) existsLocal(rf *remoteFile) bool { +func (self *syncFiles) existsLocal(rf *RemoteFile) bool {      _, found := self.findLocalByPath(rf.relPath)      return found  } -func (self *syncFiles) findRemoteByPath(relPath string) (*remoteFile, bool) { +func (self *syncFiles) findRemoteByPath(relPath string) (*RemoteFile, bool) {      if relPath == "." {          return self.root, true      } @@ -402,7 +434,7 @@ func (self *syncFiles) findRemoteByPath(relPath string) (*remoteFile, bool) {      return nil, false  } -func (self *syncFiles) findLocalByPath(relPath string) (*localFile, bool) { +func (self *syncFiles) findLocalByPath(relPath string) (*LocalFile, bool) {      for _, lf := range self.local {          if relPath == lf.relPath {              return lf, true @@ -412,7 +444,7 @@ func (self *syncFiles) findLocalByPath(relPath string) (*localFile, bool) {      return nil, false  } -type byLocalPathLength []*localFile +type byLocalPathLength []*LocalFile  func (self byLocalPathLength) Len() int {      return len(self) @@ -426,7 +458,7 @@ func (self byLocalPathLength) Less(i, j int) bool {      return pathLength(self[i].relPath) < pathLength(self[j].relPath)  } -type byRemotePathLength []*remoteFile +type byRemotePathLength []*RemoteFile  func (self byRemotePathLength) Len() int {      return len(self) diff --git a/drive/sync_download.go b/drive/sync_download.go index 7da16bd..f18cb18 100644 --- a/drive/sync_download.go +++ b/drive/sync_download.go @@ -18,6 +18,7 @@ type DownloadSyncArgs struct {      Path string      DryRun bool      DeleteExtraneous bool +    Comparer FileComparer  }  func (self *Drive) DownloadSync(args DownloadSyncArgs) error { @@ -31,7 +32,7 @@ func (self *Drive) DownloadSync(args DownloadSyncArgs) error {      }      fmt.Fprintln(args.Out, "Collecting local and remote file information...") -    files, err := self.prepareSyncFiles(args.Path, rootDir) +    files, err := self.prepareSyncFiles(args.Path, rootDir, args.Comparer)      if err != nil {          return err      } diff --git a/drive/sync_upload.go b/drive/sync_upload.go index 151a9f8..445ed42 100644 --- a/drive/sync_upload.go +++ b/drive/sync_upload.go @@ -19,6 +19,7 @@ type UploadSyncArgs struct {      DryRun bool      DeleteExtraneous bool      ChunkSize int64 +    Comparer FileComparer  }  func (self *Drive) UploadSync(args UploadSyncArgs) error { @@ -36,7 +37,7 @@ func (self *Drive) UploadSync(args UploadSyncArgs) error {      }      fmt.Fprintln(args.Out, "Collecting local and remote file information...") -    files, err := self.prepareSyncFiles(args.Path, rootDir) +    files, err := self.prepareSyncFiles(args.Path, rootDir, args.Comparer)      if err != nil {          return err      } @@ -143,7 +144,7 @@ func (self *Drive) createMissingRemoteDirs(files *syncFiles, args UploadSyncArgs          fmt.Fprintf(args.Out, "[%04d/%04d] Creating directory: %s\n", i + 1, missingCount, filepath.Join(files.root.file.Name, lf.relPath))          if args.DryRun { -            files.remote = append(files.remote, &remoteFile{ +            files.remote = append(files.remote, &RemoteFile{                  relPath: lf.relPath,                  file: dstFile,              }) @@ -153,7 +154,7 @@ func (self *Drive) createMissingRemoteDirs(files *syncFiles, args UploadSyncArgs                  return nil, fmt.Errorf("Failed to create directory: %s", err)              } -            files.remote = append(files.remote, &remoteFile{ +            files.remote = append(files.remote, &RemoteFile{                  relPath: lf.relPath,                  file: f,              }) @@ -244,7 +245,7 @@ func (self *Drive) deleteExtraneousRemoteFiles(files *syncFiles, args UploadSync      return nil  } -func (self *Drive) uploadMissingFile(parentId string, lf *localFile, args UploadSyncArgs) error { +func (self *Drive) uploadMissingFile(parentId string, lf *LocalFile, args UploadSyncArgs) error {      srcFile, err := os.Open(lf.absPath)      if err != nil {          return fmt.Errorf("Failed to open file: %s", err) @@ -300,7 +301,7 @@ func (self *Drive) updateChangedFile(cf *changedFile, args UploadSyncArgs) error      return nil  } -func (self *Drive) deleteRemoteFile(rf *remoteFile, args UploadSyncArgs) error { +func (self *Drive) deleteRemoteFile(rf *RemoteFile, args UploadSyncArgs) error {      err := self.service.Files.Delete(rf.file.Id).Do()      if err != nil {          return fmt.Errorf("Failed to delete file: %s", err) diff --git a/drive/util.go b/drive/util.go index 1c43009..2116f9b 100644 --- a/drive/util.go +++ b/drive/util.go @@ -9,8 +9,6 @@ import (      "unicode/utf8"      "math"      "time" -    "crypto/md5" -    "io"  )  type kv struct { @@ -137,18 +135,6 @@ func intMax() int64 {      return 1 << (strconv.IntSize - 1) - 1  } -func md5sum(path string) string { -    h := md5.New() -    f, err := os.Open(path) -    if err != nil { -        return "" -    } -    defer f.Close() - -    io.Copy(h, f) -    return fmt.Sprintf("%x", h.Sum(nil)) -} -  func pathLength(path string) int {      return strings.Count(path, string(os.PathSeparator))  } diff --git a/handlers_drive.go b/handlers_drive.go index 8e7540c..029b47e 100644 --- a/handlers_drive.go +++ b/handlers_drive.go @@ -65,6 +65,7 @@ func downloadSyncHandler(ctx cli.Context) {          RootId: args.String("id"),          DryRun: args.Bool("dryRun"),          DeleteExtraneous: args.Bool("deleteExtraneous"), +        Comparer: Md5Comparer{},      })      checkErr(err)  } @@ -122,6 +123,7 @@ func uploadSyncHandler(ctx cli.Context) {          DryRun: args.Bool("dryRun"),          DeleteExtraneous: args.Bool("deleteExtraneous"),          ChunkSize: args.Int64("chunksize"), +        Comparer: Md5Comparer{},      })      checkErr(err)  } | 
