Skip to content

Commit

Permalink
Merge branch 'naive-conflict-resolve'
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Odeke committed Feb 7, 2015
2 parents 5ab0e58 + 2029639 commit 698cb89
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 15 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ $ cd ~/gdrive
### Pulling

The `pull` command downloads data from Google Drive that does not exist locally, and deletes local data that is not present on Google Drive.

Run it without any arguments to pull all of the files from the current path:

```shell
Expand All @@ -86,6 +85,21 @@ To force download from paths that otherwise would be marked with no-changes
$ drive pull -force
```

+ Note:
In relation to [#57](https://github.com/odeke-em/drive/issues/57) and [@rakyll's #49](https://github.com/rakyll/drive/issues/49).
A couple of scenarios in which data was getting totally clobbered and unrecoverable, drive now tries to play it safe and warn you if your data could potentially be lost e.g during a to-disk clobber for which you have no backup. At least with a push you have the luxury of untrashing content. To disable this safety, run drive with flag `-force` e.g:

```shell
$ drive pull -force collaboration_documents
```

Playing the safety card even more, if you want to get changes that are non clobberable ie only additions
run drive with flag `-no-clobber` e.g:

```shell
$ drive pull -no-clobber Makefile
```

To pull specific files or directories, pass in one or more paths:

```shell
Expand All @@ -98,6 +112,7 @@ Note: To ignore checksum verification during a pull:
$ drive pull -ignore-checksum
```


#### Exporting Docs

By default, the `pull` command will export Google Docs documents as PDF files. To specify other formats, use the `-export` option:
Expand Down Expand Up @@ -139,6 +154,18 @@ Note: To ignore checksum verification during a push:
$ drive push -ignore-checksum
```

For safety with non clobberable changes i.e only additions:

```shell
$ drive push -no-clobber
```

+ Due to the reasons explained in the pull section, drive should be able to warn you in case of total clobbers on data. To turn off this behaviour/safety, pass in the `-force` flag i.e:

```shell
$ drive push -force sure_of_content
```


To get Google Drive to convert a file to its native Google Docs format

Expand Down
27 changes: 22 additions & 5 deletions src/changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ func (g *Commands) changeListResolve(relToRoot, fsPath string, isPush bool) (cl
l = NewLocalFile(fsPath, localinfo)
}

fmt.Println("Resolving...")
return g.resolveChangeListRecv(isPush, relToRoot, relToRoot, r, l)
}

Expand Down Expand Up @@ -276,17 +275,32 @@ func reduceToSize(changes []*Change, isPush bool) (totalSize int64) {
return totalSize
}

func summarizeChanges(changes []*Change, reduce bool) {
func checkAndPrintChanges(changes []*Change, reduce bool) bool {
// Firstly detect the conflicting changes and if present return false
var conflicts []string
for _, c := range changes {
if c.Op() != OpNone {
fmt.Println(c.Symbol(), c.Path)
if c.Op() == OpModConflict {
conflicts = append(conflicts, c.Path)
}
}

conflictCount := len(conflicts)
if conflictCount >= 1 {
fmt.Printf("These %d file(s) would be overwritten. Use -%s to override this behaviour\n", conflictCount, ForceKey)
for _, path := range conflicts {
fmt.Println(path)
}
return false
}

if reduce {
opMap := map[int]sizeCounter{}

for _, c := range changes {
op := c.Op()
if op != OpNone {
fmt.Println(c.Symbol(), c.Path)
}
counter := opMap[op]
counter.count += 1
if c.Src != nil {
Expand All @@ -306,6 +320,7 @@ func summarizeChanges(changes []*Change, reduce bool) {
fmt.Printf("%s %s\n", name, counter.String())
}
}
return true
}

func promptForChanges() bool {
Expand All @@ -320,7 +335,9 @@ func printChangeList(changes []*Change, noPrompt bool, noClobber bool) bool {
fmt.Println("Everything is up-to-date.")
return false
}
summarizeChanges(changes, !noPrompt)
if !checkAndPrintChanges(changes, !noPrompt) {
return false
}

if noPrompt {
return true
Expand Down
2 changes: 2 additions & 0 deletions src/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const (
UntrashKey = "untrash"
UnpubKey = "unpub"
VersionKey = "version"

ForceKey = "force"
)

const (
Expand Down
2 changes: 2 additions & 0 deletions src/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const (
// It doesn't check if there are remote changes if isForce is set.
func (g *Commands) Pull() (err error) {
var cl []*Change

fmt.Println("Resolving...")
for _, relToRootPath := range g.opts.Sources {
fsPath := g.context.AbsPathOf(relToRootPath)
ccl, cErr := g.changeListResolve(relToRootPath, fsPath, false)
Expand Down
1 change: 1 addition & 0 deletions src/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (g *Commands) Push() (err error) {
root := g.context.AbsPathOf("")
var cl []*Change

fmt.Println("Resolving...")
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)

Expand Down
1 change: 1 addition & 0 deletions src/stat.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func prettyFileStat(relToRootPath string, file *File) {
&keyValue{"Size", prettyBytes(file.Size)},
&keyValue{"DirType", dirType},
&keyValue{"MimeType", file.MimeType},
&keyValue{"Etag", file.Etag},
&keyValue{"ModTime", fmt.Sprintf("%v", file.ModTime)},
}
if !file.IsDir {
Expand Down
36 changes: 27 additions & 9 deletions src/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
OpAdd
OpDelete
OpMod
OpModConflict
)

const (
Expand All @@ -47,10 +48,11 @@ const (
var BigFileSize = int64(1024 * 1024 * 400)

var opPrecedence = map[int]int{
OpNone: 0,
OpDelete: 1,
OpAdd: 2,
OpMod: 3,
OpNone: 0,
OpDelete: 1,
OpAdd: 2,
OpMod: 3,
OpModConflict: 4,
}

type File struct {
Expand Down Expand Up @@ -121,6 +123,7 @@ type Change struct {
Src *File
Force bool
NoClobber bool
IgnoreConflict bool
IgnoreChecksum bool
}

Expand Down Expand Up @@ -158,6 +161,8 @@ func opToString(op int) (string, string) {
return "\033[31m-\033[0m", "Deletion"
case OpMod:
return "\033[33mM\033[0m", "Modification"
case OpModConflict:
return "\033[35mX\033[0m", "Clashing modification"
default:
return "", ""
}
Expand Down Expand Up @@ -238,16 +243,14 @@ func fileDifferences(src, dest *File, ignoreChecksum bool) int {
}

if !ignoreChecksum {
// Only compute the checksum if the size is the same.
// Only compute the checksum if the size differs
if sizeDiffers(difference) || md5Checksum(src) != md5Checksum(dest) {
difference |= DifferMd5Checksum
}
}
return difference
}

// If the preliminary sameFile test passes,
// then perform an Md5 checksum comparison
func sameFileTillChecksum(src, dest *File, ignoreChecksum bool) bool {
return fileDifferences(src, dest, ignoreChecksum) == DifferNone
}
Expand All @@ -265,18 +268,33 @@ func (c *Change) op() int {
if c.Src.IsDir != c.Dest.IsDir {
return OpMod
}
if c.Src.IsDir {
return OpNone
}

mask := fileDifferences(c.Src, c.Dest, c.IgnoreChecksum)

if !c.Src.IsDir && !sameFileTillChecksum(c.Src, c.Dest, c.IgnoreChecksum) {
if sizeDiffers(mask) || checksumDiffers(mask) {
if c.IgnoreConflict {
return OpMod
}
return OpModConflict
}
if modTimeDiffers(mask) {
return OpMod
}

return OpNone
}

func (c *Change) Op() int {
op := c.op()
if c.Force {
if op == OpModConflict {
return OpMod
}
return OpAdd
}
op := c.op()
if op != OpAdd && c.NoClobber {
return OpNone
}
Expand Down

0 comments on commit 698cb89

Please sign in to comment.