aboutsummaryrefslogtreecommitdiffstats
path: root/drive/sync_upload.go
diff options
context:
space:
mode:
authorPetter Rasmussen2016-02-13 01:09:47 +0100
committerPetter Rasmussen2016-02-13 01:10:17 +0100
commit12e431b5e17ff4ceb6680987bd4649a94b83f896 (patch)
treeae18b93e74026fe60f4b02a2be9839e32fc4231d /drive/sync_upload.go
parent5eae4f159d340d257e41e75604a0fc831cb76381 (diff)
downloadgdrive-12e431b5e17ff4ceb6680987bd4649a94b83f896.tar.bz2
Add conflict handling and flags for uploads
Diffstat (limited to 'drive/sync_upload.go')
-rw-r--r--drive/sync_upload.go77
1 files changed, 73 insertions, 4 deletions
diff --git a/drive/sync_upload.go b/drive/sync_upload.go
index ca81b5c..949d8a6 100644
--- a/drive/sync_upload.go
+++ b/drive/sync_upload.go
@@ -6,6 +6,7 @@ import (
"os"
"time"
"sort"
+ "bytes"
"path/filepath"
"google.golang.org/api/googleapi"
"google.golang.org/api/drive/v3"
@@ -19,6 +20,7 @@ type UploadSyncArgs struct {
DryRun bool
DeleteExtraneous bool
ChunkSize int64
+ Resolution ConflictResolution
Comparer FileComparer
}
@@ -42,8 +44,19 @@ func (self *Drive) UploadSync(args UploadSyncArgs) error {
return err
}
+ // Find changed files
+ changedFiles := files.filterChangedLocalFiles()
+
fmt.Fprintf(args.Out, "Found %d local files and %d remote files\n", len(files.local), len(files.remote))
+ // Ensure that that we don't overwrite any remote changes
+ if args.Resolution == NoResolution {
+ err = ensureNoRemoteModifications(changedFiles)
+ if err != nil {
+ return fmt.Errorf("Conflict detected!\nThe following files have changed and the remote file are newer than it's local counterpart:\n\n%s\nNo conflict resolution was given, aborting...", err)
+ }
+ }
+
// Create missing directories
files, err = self.createMissingRemoteDirs(files, args)
if err != nil {
@@ -57,7 +70,7 @@ func (self *Drive) UploadSync(args UploadSyncArgs) error {
}
// Update modified files
- err = self.updateChangedFiles(files, args)
+ err = self.updateChangedFiles(changedFiles, rootDir, args)
if err != nil {
return err
}
@@ -194,8 +207,7 @@ func (self *Drive) uploadMissingFiles(files *syncFiles, args UploadSyncArgs) err
return nil
}
-func (self *Drive) updateChangedFiles(files *syncFiles, args UploadSyncArgs) error {
- changedFiles := files.filterChangedLocalFiles()
+func (self *Drive) updateChangedFiles(changedFiles []*changedFile, root *drive.File, args UploadSyncArgs) error {
changedCount := len(changedFiles)
if changedCount > 0 {
@@ -203,7 +215,12 @@ func (self *Drive) updateChangedFiles(files *syncFiles, args UploadSyncArgs) err
}
for i, cf := range changedFiles {
- fmt.Fprintf(args.Out, "[%04d/%04d] Updating %s -> %s\n", i + 1, changedCount, cf.local.relPath, filepath.Join(files.root.file.Name, cf.local.relPath))
+ if skip, reason := checkRemoteConflict(cf, args.Resolution); skip {
+ fmt.Fprintf(args.Out, "[%04d/%04d] Skipping %s (%s)\n", i + 1, changedCount, cf.local.relPath, reason)
+ continue
+ }
+
+ fmt.Fprintf(args.Out, "[%04d/%04d] Updating %s -> %s\n", i + 1, changedCount, cf.local.relPath, filepath.Join(root.Name, cf.local.relPath))
if args.DryRun {
continue
@@ -363,3 +380,55 @@ func (self *Drive) dirIsEmpty(id string) (bool, error) {
return len(fileList.Files) == 0, nil
}
+
+func checkRemoteConflict(cf *changedFile, resolution ConflictResolution) (bool, string) {
+ // No conflict unless remote file was last modified
+ if cf.compareModTime() != RemoteLastModified {
+ return false, ""
+ }
+
+ // Don't skip if want to keep the local file
+ if resolution == KeepLocal {
+ return false, ""
+ }
+
+ // Skip if we want to keep the remote file
+ if resolution == KeepRemote {
+ return true, "conflicting file, keeping remote file"
+ }
+
+ if resolution == KeepLargest {
+ largest := cf.compareSize()
+
+ // Skip if the remote file is largest
+ if largest == RemoteLargestSize {
+ return true, "conflicting file, remote file is largest, keeping remote"
+ }
+
+ // Don't skip if the local file is largest
+ if largest == LocalLargestSize {
+ return false, ""
+ }
+
+ // Keep remote if both files have the same size
+ if largest == EqualSize {
+ return true, "conflicting file, file sizes are equal, keeping remote"
+ }
+ }
+
+ // The conditionals above should cover all cases,
+ // unless the programmer did something wrong,
+ // in which case we default to being non-destructive and skip the file
+ return true, "conflicting file, unhandled case"
+}
+
+func ensureNoRemoteModifications(files []*changedFile) error {
+ conflicts := findRemoteConflicts(files)
+ if len(conflicts) == 0 {
+ return nil
+ }
+
+ buffer := bytes.NewBufferString("")
+ formatConflicts(conflicts, buffer)
+ return fmt.Errorf(buffer.String())
+}