From 68f09986999824d247d084f3aed81ec957cd0a93 Mon Sep 17 00:00:00 2001 From: nielash Date: Fri, 15 Dec 2023 07:47:15 -0500 Subject: [PATCH] bisync: add options to auto-resolve conflicts - fixes #7471 Before this change, when a file was new/changed on both paths (relative to the prior sync), and the versions on each side were not identical, bisync would keep both versions, renaming them with ..path1 and ..path2 suffixes, respectively. Many users have requested more control over how bisync handles such conflicts -- including an option to automatically select one version as the "winner" and rename or delete the "loser". This change introduces support for such options. --conflict-resolve CHOICE In bisync, a "conflict" is a file that is *new* or *changed* on *both sides* (relative to the prior run) AND is *not currently identical* on both sides. `--conflict-resolve` controls how bisync handles such a scenario. The currently supported options are: - `none` - (the default) - do not attempt to pick a winner, keep and rename both files according to `--conflict-loser` and `--conflict-suffix` settings. For example, with the default settings, `file.txt` on Path1 is renamed `file.txt.conflict1` and `file.txt` on Path2 is renamed `file.txt.conflict2`. Both are copied to the opposite path during the run, so both sides end up with a copy of both files. (As `none` is the default, it is not necessary to specify `--conflict-resolve none` -- you can just omit the flag.) - `newer` - the newer file (by `modtime`) is considered the winner and is copied without renaming. The older file (the "loser") is handled according to `--conflict-loser` and `--conflict-suffix` settings (either renamed or deleted.) For example, if `file.txt` on Path1 is newer than `file.txt` on Path2, the result on both sides (with other default settings) will be `file.txt` (winner from Path1) and `file.txt.conflict1` (loser from Path2). - `older` - same as `newer`, except the older file is considered the winner, and the newer file is considered the loser. - `larger` - the larger file (by `size`) is considered the winner (regardless of `modtime`, if any). - `smaller` - the smaller file (by `size`) is considered the winner (regardless of `modtime`, if any). - `path1` - the version from Path1 is unconditionally considered the winner (regardless of `modtime` and `size`, if any). This can be useful if one side is usually more trusted or up-to-date than the other. - `path2` - same as `path1`, except the path2 version is considered the winner. For all of the above options, note the following: - If either of the underlying remotes lacks support for the chosen method, it will be ignored and fall back to `none`. (For example, if `--conflict-resolve newer` is set, but one of the paths uses a remote that doesn't support `modtime`.) - If a winner can't be determined because the chosen method's attribute is missing or equal, it will be ignored and fall back to `none`. (For example, if `--conflict-resolve newer` is set, but the Path1 and Path2 modtimes are identical, even if the sizes may differ.) - If the file's content is currently identical on both sides, it is not considered a "conflict", even if new or changed on both sides since the prior sync. (For example, if you made a change on one side and then synced it to the other side by other means.) Therefore, none of the conflict resolution flags apply in this scenario. - The conflict resolution flags do not apply during a `--resync`, as there is no "prior run" to speak of (but see `--resync-mode` for similar options.) --conflict-loser CHOICE `--conflict-loser` determines what happens to the "loser" of a sync conflict (when `--conflict-resolve` determines a winner) or to both files (when there is no winner.) The currently supported options are: - `num` - (the default) - auto-number the conflicts by automatically appending the next available number to the `--conflict-suffix`, in chronological order. For example, with the default settings, the first conflict for `file.txt` will be renamed `file.txt.conflict1`. If `file.txt.conflict1` already exists, `file.txt.conflict2` will be used instead (etc., up to a maximum of 9223372036854775807 conflicts.) - `pathname` - rename the conflicts according to which side they came from, which was the default behavior prior to `v1.66`. For example, with `--conflict-suffix path`, `file.txt` from Path1 will be renamed `file.txt.path1`, and `file.txt` from Path2 will be renamed `file.txt.path2`. If two non-identical suffixes are provided (ex. `--conflict-suffix cloud,local`), the trailing digit is omitted. Importantly, note that with `pathname`, there is no auto-numbering beyond `2`, so if `file.txt.path2` somehow already exists, it will be overwritten. Using a dynamic date variable in your `--conflict-suffix` (see below) is one possible way to avoid this. Note also that conflicts-of-conflicts are possible, if the original conflict is not manually resolved -- for example, if for some reason you edited `file.txt.path1` on both sides, and those edits were different, the result would be `file.txt.path1.path1` and `file.txt.path1.path2` (in addition to `file.txt.path2`.) - `delete` - keep the winner only and delete the loser, instead of renaming it. If a winner cannot be determined (see `--conflict-resolve` for details on how this could happen), `delete` is ignored and the default `num` is used instead (i.e. both versions are kept and renamed, and neither is deleted.) `delete` is inherently the most destructive option, so use it only with care. For all of the above options, note that if a winner cannot be determined (see `--conflict-resolve` for details on how this could happen), or if `--conflict-resolve` is not in use, *both* files will be renamed. --conflict-suffix STRING[,STRING] `--conflict-suffix` controls the suffix that is appended when bisync renames a `--conflict-loser` (default: `conflict`). `--conflict-suffix` will accept either one string or two comma-separated strings to assign different suffixes to Path1 vs. Path2. This may be helpful later in identifying the source of the conflict. (For example, `--conflict-suffix dropboxconflict,laptopconflict`) With `--conflict-loser num`, a number is always appended to the suffix. With `--conflict-loser pathname`, a number is appended only when one suffix is specified (or when two identical suffixes are specified.) i.e. with `--conflict-loser pathname`, all of the following would produce exactly the same result: ``` --conflict-suffix path --conflict-suffix path,path --conflict-suffix path1,path2 ``` Suffixes may be as short as 1 character. By default, the suffix is appended after any other extensions (ex. `file.jpg.conflict1`), however, this can be changed with the `--suffix-keep-extension` flag (i.e. to instead result in `file.conflict1.jpg`). `--conflict-suffix` supports several *dynamic date variables* when enclosed in curly braces as globs. This can be helpful to track the date and/or time that each conflict was handled by bisync. For example: ``` --conflict-suffix {DateOnly}-conflict // result: myfile.txt.2006-01-02-conflict1 ``` All of the formats described [here](https://pkg.go.dev/time#pkg-constants) and [here](https://pkg.go.dev/time#example-Time.Format) are supported, but take care to ensure that your chosen format does not use any characters that are illegal on your remotes (for example, macOS does not allow colons in filenames, and slashes are also best avoided as they are often interpreted as directory separators.) To address this particular issue, an additional `{MacFriendlyTime}` (or just `{mac}`) option is supported, which results in `2006-01-02 0304PM`. Note that `--conflict-suffix` is entirely separate from rclone's main `--sufix` flag. This is intentional, as users may wish to use both flags simultaneously, if also using `--backup-dir`. Finally, note that the default in bisync prior to `v1.66` was to rename conflicts with `..path1` and `..path2` (with two periods, and `path` instead of `conflict`.) Bisync now defaults to a single dot instead of a double dot, but additional dots can be added by including them in the specified suffix string. For example, for behavior equivalent to the previous default, use: ``` [--conflict-resolve none] --conflict-loser pathname --conflict-suffix .path ``` --- cmd/bisync/bilib/names.go | 80 ++++ cmd/bisync/bisync_test.go | 9 + cmd/bisync/cmd.go | 10 + cmd/bisync/deltas.go | 45 +- cmd/bisync/listing.go | 52 +- cmd/bisync/operations.go | 7 +- cmd/bisync/resolve.go | 450 ++++++++++++++++++ ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- ...estdir_path1.._testdir_path2.path1.lst-err | 4 +- ...estdir_path1.._testdir_path2.path1.lst-new | 4 +- ...estdir_path1.._testdir_path2.path1.lst-old | 4 +- ...estdir_path1.._testdir_path2.path2.lst-err | 4 +- ...estdir_path1.._testdir_path2.path2.lst-new | 4 +- ...estdir_path1.._testdir_path2.path2.lst-old | 4 +- .../testdata/test_backupdir/golden/test.log | 8 +- ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../_testdir_path1.._testdir_path2.path1.lst | 4 +- .../_testdir_path1.._testdir_path2.path2.lst | 4 +- .../testdata/test_changes/golden/test.log | 8 +- ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../_testdir_path1.._testdir_path2.path1.lst | 4 +- .../_testdir_path1.._testdir_path2.path2.lst | 4 +- .../testdata/test_compare_all/golden/test.log | 8 +- ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../_testdir_path1.._testdir_path2.path1.lst | 4 +- .../_testdir_path1.._testdir_path2.path2.lst | 4 +- ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../testdata/test_dry_run/golden/test.log | 18 +- ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../_testdir_path1.._testdir_path2.path1.lst | 4 +- .../_testdir_path1.._testdir_path2.path2.lst | 4 +- .../testdata/test_equal/golden/test.log | 8 +- ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../_testdir_path1.._testdir_path2.path1.lst | 4 +- .../_testdir_path1.._testdir_path2.path2.lst | 4 +- .../test_extended_filenames/golden/test.log | 8 +- ...testdir_path1.._testdir_path2.copy1to2.que | 1 + ...testdir_path1.._testdir_path2.copy2to1.que | 1 + .../_testdir_path1.._testdir_path2.path1.lst | 10 + ...estdir_path1.._testdir_path2.path1.lst-new | 11 + ...estdir_path1.._testdir_path2.path1.lst-old | 10 + .../_testdir_path1.._testdir_path2.path2.lst | 10 + ...estdir_path1.._testdir_path2.path2.lst-new | 11 + ...estdir_path1.._testdir_path2.path2.lst-old | 10 + .../testdata/test_resolve/golden/test.log | 304 ++++++++++++ .../testdata/test_resolve/initial/RCLONE_TEST | 1 + .../testdata/test_resolve/initial/file1.txt | 0 .../testdata/test_resolve/initial/file2.txt | 0 .../testdata/test_resolve/modfiles/file1L.txt | 1 + .../testdata/test_resolve/modfiles/file1R.txt | 1 + .../testdata/test_resolve/modfiles/file2.txt | 1 + cmd/bisync/testdata/test_resolve/scenario.txt | 67 +++ ...testdir_path1.._testdir_path2.copy1to2.que | 2 +- ...testdir_path1.._testdir_path2.copy2to1.que | 2 +- .../_testdir_path1.._testdir_path2.path1.lst | 8 +- ...estdir_path1.._testdir_path2.path1.lst-new | 6 +- ...estdir_path1.._testdir_path2.path1.lst-old | 6 +- .../_testdir_path1.._testdir_path2.path2.lst | 8 +- ...estdir_path1.._testdir_path2.path2.lst-new | 6 +- ...estdir_path1.._testdir_path2.path2.lst-old | 6 +- .../testdata/test_volatile/golden/test.log | 24 +- docs/content/bisync.md | 158 +++++- 69 files changed, 1304 insertions(+), 164 deletions(-) create mode 100644 cmd/bisync/resolve.go create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy1to2.que create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy2to1.que create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-new create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-old create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-new create mode 100644 cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-old create mode 100644 cmd/bisync/testdata/test_resolve/golden/test.log create mode 100644 cmd/bisync/testdata/test_resolve/initial/RCLONE_TEST create mode 100644 cmd/bisync/testdata/test_resolve/initial/file1.txt create mode 100644 cmd/bisync/testdata/test_resolve/initial/file2.txt create mode 100644 cmd/bisync/testdata/test_resolve/modfiles/file1L.txt create mode 100644 cmd/bisync/testdata/test_resolve/modfiles/file1R.txt create mode 100644 cmd/bisync/testdata/test_resolve/modfiles/file2.txt create mode 100644 cmd/bisync/testdata/test_resolve/scenario.txt diff --git a/cmd/bisync/bilib/names.go b/cmd/bisync/bilib/names.go index 3093dff55..70d3d61be 100644 --- a/cmd/bisync/bilib/names.go +++ b/cmd/bisync/bilib/names.go @@ -5,6 +5,8 @@ import ( "os" "sort" "strconv" + "strings" + "time" ) // Names comprises a set of file names @@ -83,3 +85,81 @@ func (am AliasMap) Alias(name1 string) string { } return name1 } + +// ParseGlobs determines whether a string contains {brackets} +// and returns the substring (including both brackets) for replacing +// substring is first opening bracket to last closing bracket -- +// good for {{this}} but not {this}{this} +func ParseGlobs(s string) (hasGlobs bool, substring string) { + open := strings.Index(s, "{") + close := strings.LastIndex(s, "}") + if open >= 0 && close > open { + return true, s[open : close+1] + } + return false, "" +} + +// TrimBrackets converts {{this}} to this +func TrimBrackets(s string) string { + return strings.Trim(s, "{}") +} + +// TimeFormat converts a user-supplied string to a Go time constant, if possible +func TimeFormat(timeFormat string) string { + switch timeFormat { + case "Layout": + timeFormat = time.Layout + case "ANSIC": + timeFormat = time.ANSIC + case "UnixDate": + timeFormat = time.UnixDate + case "RubyDate": + timeFormat = time.RubyDate + case "RFC822": + timeFormat = time.RFC822 + case "RFC822Z": + timeFormat = time.RFC822Z + case "RFC850": + timeFormat = time.RFC850 + case "RFC1123": + timeFormat = time.RFC1123 + case "RFC1123Z": + timeFormat = time.RFC1123Z + case "RFC3339": + timeFormat = time.RFC3339 + case "RFC3339Nano": + timeFormat = time.RFC3339Nano + case "Kitchen": + timeFormat = time.Kitchen + case "Stamp": + timeFormat = time.Stamp + case "StampMilli": + timeFormat = time.StampMilli + case "StampMicro": + timeFormat = time.StampMicro + case "StampNano": + timeFormat = time.StampNano + case "DateTime": + // timeFormat = time.DateTime // missing in go1.19 + timeFormat = "2006-01-02 15:04:05" + case "DateOnly": + // timeFormat = time.DateOnly // missing in go1.19 + timeFormat = "2006-01-02" + case "TimeOnly": + // timeFormat = time.TimeOnly // missing in go1.19 + timeFormat = "15:04:05" + case "MacFriendlyTime", "macfriendlytime", "mac": + timeFormat = "2006-01-02 0304PM" // not actually a Go constant -- but useful as macOS filenames can't have colons + } + return timeFormat +} + +// AppyTimeGlobs converts "myfile-{DateOnly}.txt" to "myfile-2006-01-02.txt" +func AppyTimeGlobs(s string, t time.Time) string { + hasGlobs, substring := ParseGlobs(s) + if !hasGlobs { + return s + } + timeString := t.Local().Format(TimeFormat(TrimBrackets(substring))) + return strings.ReplaceAll(s, substring, timeString) +} diff --git a/cmd/bisync/bisync_test.go b/cmd/bisync/bisync_test.go index 68df96838..a1d21845c 100644 --- a/cmd/bisync/bisync_test.go +++ b/cmd/bisync/bisync_test.go @@ -118,6 +118,9 @@ var logHoppers = []string{ // Test case `normalization` can have random order of fix-case files. `(?:INFO |NOTICE): .*: Fixed case by renaming to: .*`, + + // order of files re-checked prior to a conflict rename + `ERROR : .*: md5 differ.*`, } // Some log lines can contain Windows path separator that must be @@ -853,6 +856,12 @@ func (b *bisyncTest) runBisync(ctx context.Context, args []string) (err error) { ci.NoUnicodeNormalization = false ci.IgnoreCaseSync = true ci.FixCase = true + case "conflict-resolve": + _ = opt.ConflictResolve.Set(val) + case "conflict-loser": + _ = opt.ConflictLoser.Set(val) + case "conflict-suffix": + opt.ConflictSuffixFlag = val default: return fmt.Errorf("invalid bisync option %q", arg) } diff --git a/cmd/bisync/cmd.go b/cmd/bisync/cmd.go index 6e5b95912..17442c9ea 100644 --- a/cmd/bisync/cmd.go +++ b/cmd/bisync/cmd.go @@ -55,6 +55,11 @@ type Options struct { CompareFlag string DebugName string MaxLock time.Duration + ConflictResolve Prefer + ConflictLoser ConflictLoserAction + ConflictSuffixFlag string + ConflictSuffix1 string + ConflictSuffix2 string } // Default values @@ -141,6 +146,9 @@ func init() { flags.BoolVarP(cmdFlags, &Opt.Compare.SlowHashSyncOnly, "slow-hash-sync-only", "", Opt.Compare.SlowHashSyncOnly, "Ignore slow checksums for listings and deltas, but still consider them during sync calls.", "") flags.BoolVarP(cmdFlags, &Opt.Compare.DownloadHash, "download-hash", "", Opt.Compare.DownloadHash, "Compute hash by downloading when otherwise unavailable. (warning: may be slow and use lots of data!)", "") flags.DurationVarP(cmdFlags, &Opt.MaxLock, "max-lock", "", Opt.MaxLock, "Consider lock files older than this to be expired (default: 0 (never expire)) (minimum: 2m)", "") + flags.FVarP(cmdFlags, &Opt.ConflictResolve, "conflict-resolve", "", "Automatically resolve conflicts by preferring the version that is: "+ConflictResolveList+" (default: none)", "") + flags.FVarP(cmdFlags, &Opt.ConflictLoser, "conflict-loser", "", "Action to take on the loser of a sync conflict (when there is a winner) or on both files (when there is no winner): "+ConflictLoserList+" (default: num)", "") + flags.StringVarP(cmdFlags, &Opt.ConflictSuffixFlag, "conflict-suffix", "", Opt.ConflictSuffixFlag, "Suffix to use when renaming a --conflict-loser. Can be either one string or two comma-separated strings to assign different suffixes to Path1/Path2. (default: 'conflict')", "") } // bisync command definition @@ -154,6 +162,8 @@ var commandDefinition = &cobra.Command{ "status": "Beta", }, RunE: func(command *cobra.Command, args []string) error { + // NOTE: avoid putting too much handling here, as it won't apply to the rc. + // Generally it's best to put init-type stuff in Bisync() (operations.go) cmd.CheckArgs(2, 2, command, args) fs1, file1, fs2, file2 := cmd.NewFsSrcDstFiles(args) if file1 != "" || file2 != "" { diff --git a/cmd/bisync/deltas.go b/cmd/bisync/deltas.go index 323d1c799..167bd70fd 100644 --- a/cmd/bisync/deltas.go +++ b/cmd/bisync/deltas.go @@ -8,11 +8,11 @@ import ( "path/filepath" "sort" "strings" + "time" "github.com/rclone/rclone/cmd/bisync/bilib" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/filter" - "github.com/rclone/rclone/fs/operations" "github.com/rclone/rclone/lib/terminal" "golang.org/x/text/unicode/norm" ) @@ -46,6 +46,7 @@ func (d delta) is(cond delta) bool { type deltaSet struct { deltas map[string]delta size map[string]int64 + time map[string]time.Time hash map[string]string opt *Options fs fs.Fs // base filesystem @@ -170,6 +171,7 @@ func (b *bisyncRun) findDeltas(fctx context.Context, f fs.Fs, oldListing string, ds = &deltaSet{ deltas: map[string]delta{}, size: map[string]int64{}, + time: map[string]time.Time{}, hash: map[string]string{}, fs: f, msg: msg, @@ -183,6 +185,7 @@ func (b *bisyncRun) findDeltas(fctx context.Context, f fs.Fs, oldListing string, d := deltaZero s := int64(0) h := "" + var t time.Time if !now.has(file) { b.indent(msg, file, Color(terminal.RedFg, "File was deleted")) ds.deleted++ @@ -215,6 +218,7 @@ func (b *bisyncRun) findDeltas(fctx context.Context, f fs.Fs, oldListing string, whatchanged = append(whatchanged, Color(terminal.MagentaFg, "time (older)")) d |= deltaOlder } + t = now.getTime(file) } } if b.opt.Compare.Checksum { @@ -238,6 +242,9 @@ func (b *bisyncRun) findDeltas(fctx context.Context, f fs.Fs, oldListing string, if b.opt.Compare.Size { ds.size[file] = s } + if b.opt.Compare.Modtime { + ds.time[file] = t + } if b.opt.Compare.Checksum { ds.hash[file] = h } @@ -258,6 +265,9 @@ func (b *bisyncRun) findDeltas(fctx context.Context, f fs.Fs, oldListing string, if b.opt.Compare.Size { ds.size[file] = now.getSize(file) } + if b.opt.Compare.Modtime { + ds.time[file] = now.getTime(file) + } if b.opt.Compare.Checksum { ds.hash[file] = now.getHash(file) } @@ -287,12 +297,11 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change delete1 := bilib.Names{} delete2 := bilib.Names{} handled := bilib.Names{} - renamed1 := bilib.Names{} - renamed2 := bilib.Names{} renameSkipped := bilib.Names{} deletedonboth := bilib.Names{} skippedDirs1 := newFileList() skippedDirs2 := newFileList() + b.renames = renames{} ctxMove := b.opt.setDryRun(ctx) @@ -422,34 +431,10 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change } } else { fs.Debugf(nil, "Files are NOT equal: %s", file) - b.indent("!Path1", p1+"..path1", "Renaming Path1 copy") - ctxMove = b.setBackupDir(ctxMove, 1) // in case already a file with new name - if err = operations.MoveFile(ctxMove, b.fs1, b.fs1, file+"..path1", file); err != nil { - err = fmt.Errorf("path1 rename failed for %s: %w", p1, err) - b.critical = true + err = b.resolve(ctxMove, path1, path2, file, alias, &renameSkipped, ©1to2, ©2to1, ds1, ds2) + if err != nil { return } - if b.opt.DryRun { - renameSkipped.Add(file) - } else { - renamed1.Add(file) - } - b.indent("!Path1", p2+"..path1", "Queue copy to Path2") - copy1to2.Add(file + "..path1") - - b.indent("!Path2", p2+"..path2", "Renaming Path2 copy") - ctxMove = b.setBackupDir(ctxMove, 2) // in case already a file with new name - if err = operations.MoveFile(ctxMove, b.fs2, b.fs2, alias+"..path2", alias); err != nil { - err = fmt.Errorf("path2 rename failed for %s: %w", alias, err) - return - } - if b.opt.DryRun { - renameSkipped.Add(alias) - } else { - renamed2.Add(alias) - } - b.indent("!Path2", p1+"..path2", "Queue copy to Path1") - copy2to1.Add(alias + "..path2") } } handled.Add(file) @@ -554,8 +539,6 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change queues.copy1to2 = copy1to2 queues.copy2to1 = copy2to1 - queues.renamed1 = renamed1 - queues.renamed2 = renamed2 queues.renameSkipped = renameSkipped queues.deletedonboth = deletedonboth queues.skippedDirs1 = skippedDirs1 diff --git a/cmd/bisync/listing.go b/cmd/bisync/listing.go index 827a2e14c..8f6d8eb19 100644 --- a/cmd/bisync/listing.go +++ b/cmd/bisync/listing.go @@ -77,6 +77,10 @@ func (ls *fileList) empty() bool { } func (ls *fileList) has(file string) bool { + if file == "" { + fs.Debugf(nil, "called ls.has() with blank string") + return false + } _, found := ls.info[file] if !found { //try unquoting @@ -467,11 +471,9 @@ func ConvertPrecision(Modtime time.Time, dst fs.Fs) time.Time { // modifyListing will modify the listing based on the results of the sync func (b *bisyncRun) modifyListing(ctx context.Context, src fs.Fs, dst fs.Fs, results []Results, queues queues, is1to2 bool) (err error) { queue := queues.copy2to1 - renames := queues.renamed2 direction := "2to1" if is1to2 { queue = queues.copy1to2 - renames = queues.renamed1 direction = "1to2" } @@ -596,37 +598,39 @@ func (b *bisyncRun) modifyListing(ctx context.Context, src fs.Fs, dst fs.Fs, res dstList.remove(file) } } - if renames.NotEmpty() && !b.opt.DryRun { + if b.renames.NotEmpty() && !b.opt.DryRun { // renamed on src and copied to dst - renamesList := renames.ToList() - for _, file := range renamesList { + for _, rename := range b.renames { + srcOldName, srcNewName, dstOldName, dstNewName := rename.getNames(is1to2) + fs.Debugf(nil, "%s: srcOldName: %v srcNewName: %v dstOldName: %v dstNewName: %v", direction, srcOldName, srcNewName, dstOldName, dstNewName) // we'll handle the other side when we go the other direction - newName := file + "..path2" - oppositeName := file + "..path1" - if is1to2 { - newName = file + "..path1" - oppositeName = file + "..path2" - } var new *fileInfo - // we prefer to get the info from the ..path1 / ..path2 versions + // we prefer to get the info from the newNamed versions // since they were actually copied as opposed to operations.MoveFile()'d. // the size/time/hash info is therefore fresher on the renames // but we'll settle for the original if we have to. - if srcList.has(newName) { - new = srcList.get(newName) - } else if srcList.has(oppositeName) { - new = srcList.get(oppositeName) - } else if srcList.has(file) { - new = srcList.get(file) + if srcList.has(srcNewName) { + new = srcList.get(srcNewName) + } else if srcList.has(dstNewName) { + new = srcList.get(dstNewName) + } else if srcList.has(srcOldName) { + new = srcList.get(srcOldName) } else { - if err := filterRecheck.AddFile(file); err != nil { - fs.Debugf(file, "error adding file to recheck filter: %v", err) + // something's odd, so let's recheck + if err := filterRecheck.AddFile(srcOldName); err != nil { + fs.Debugf(srcOldName, "error adding file to recheck filter: %v", err) } } - srcList.put(newName, new.size, new.time, new.hash, new.id, new.flags) - dstList.put(newName, new.size, ConvertPrecision(new.time, src), new.hash, new.id, new.flags) - srcList.remove(file) - dstList.remove(file) + if srcNewName != "" { // if it was renamed and not deleted + srcList.put(srcNewName, new.size, new.time, new.hash, new.id, new.flags) + dstList.put(srcNewName, new.size, ConvertPrecision(new.time, src), new.hash, new.id, new.flags) + } + if srcNewName != srcOldName { + srcList.remove(srcOldName) + } + if srcNewName != dstOldName { + dstList.remove(dstOldName) + } } } diff --git a/cmd/bisync/operations.go b/cmd/bisync/operations.go index 15c9e32ff..4d5aa9b75 100644 --- a/cmd/bisync/operations.go +++ b/cmd/bisync/operations.go @@ -49,13 +49,12 @@ type bisyncRun struct { CancelSync context.CancelFunc DebugName string lockFile string + renames renames } type queues struct { copy1to2 bilib.Names copy2to1 bilib.Names - renamed1 bilib.Names // renamed on 1 and copied to 2 - renamed2 bilib.Names // renamed on 2 and copied to 1 renameSkipped bilib.Names // not renamed because it was equal skippedDirs1 *fileList skippedDirs2 *fileList @@ -86,6 +85,10 @@ func Bisync(ctx context.Context, fs1, fs2 fs.Fs, optArg *Options) (err error) { if err != nil { return err } + err = b.setResolveDefaults(ctx) + if err != nil { + return err + } if b.workDir, err = filepath.Abs(opt.Workdir); err != nil { return fmt.Errorf("failed to make workdir absolute: %w", err) diff --git a/cmd/bisync/resolve.go b/cmd/bisync/resolve.go new file mode 100644 index 000000000..4daa9527b --- /dev/null +++ b/cmd/bisync/resolve.go @@ -0,0 +1,450 @@ +package bisync + +import ( + "context" + "fmt" + "math" + "mime" + "path" + "strings" + "time" + + "github.com/rclone/rclone/cmd/bisync/bilib" + "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/operations" + "github.com/rclone/rclone/lib/terminal" +) + +// Prefer describes strategies for resolving sync conflicts +type Prefer = fs.Enum[preferChoices] + +// Supported --conflict-resolve strategies +const ( + PreferNone Prefer = iota + PreferPath1 + PreferPath2 + PreferNewer + PreferOlder + PreferLarger + PreferSmaller +) + +type preferChoices struct{} + +func (preferChoices) Choices() []string { + return []string{ + PreferNone: "none", + PreferNewer: "newer", + PreferOlder: "older", + PreferLarger: "larger", + PreferSmaller: "smaller", + PreferPath1: "path1", + PreferPath2: "path2", + } +} + +func (preferChoices) Type() string { + return "Prefer" +} + +// ConflictResolveList is a list of --conflict-resolve flag choices used in the help +var ConflictResolveList = Opt.ConflictResolve.Help() + +// ConflictLoserAction describes possible actions to take on the loser of a sync conflict +type ConflictLoserAction = fs.Enum[conflictLoserChoices] + +// Supported --conflict-loser actions +const ( + ConflictLoserSkip ConflictLoserAction = iota // Reserved as zero but currently unused + ConflictLoserNumber // file.conflict1, file.conflict2, file.conflict3, etc. + ConflictLoserPathname // file.path1, file.path2 + ConflictLoserDelete // delete the loser, keep winner only +) + +type conflictLoserChoices struct{} + +func (conflictLoserChoices) Choices() []string { + return []string{ + ConflictLoserNumber: "num", + ConflictLoserPathname: "pathname", + ConflictLoserDelete: "delete", + } +} + +func (conflictLoserChoices) Type() string { + return "ConflictLoserAction" +} + +// ConflictLoserList is a list of --conflict-loser flag choices used in the help +var ConflictLoserList = Opt.ConflictLoser.Help() + +func (b *bisyncRun) setResolveDefaults(ctx context.Context) error { + if b.opt.ConflictLoser == ConflictLoserSkip { + b.opt.ConflictLoser = ConflictLoserNumber + } + if b.opt.ConflictSuffixFlag == "" { + b.opt.ConflictSuffixFlag = "conflict" + } + suffixes := strings.Split(b.opt.ConflictSuffixFlag, ",") + if len(suffixes) == 1 { + b.opt.ConflictSuffix1 = suffixes[0] + b.opt.ConflictSuffix2 = suffixes[0] + } else if len(suffixes) == 2 { + b.opt.ConflictSuffix1 = suffixes[0] + b.opt.ConflictSuffix2 = suffixes[1] + } else { + return fmt.Errorf("--conflict-suffix cannot have more than 2 comma-separated values. Received %v: %v", len(suffixes), suffixes) + } + // replace glob variables, if any + t := time.Now() // capture static time here so it is the same for all files throughout this run + b.opt.ConflictSuffix1 = bilib.AppyTimeGlobs(b.opt.ConflictSuffix1, t) + b.opt.ConflictSuffix2 = bilib.AppyTimeGlobs(b.opt.ConflictSuffix2, t) + + // append dot (intentionally allow more than one) + b.opt.ConflictSuffix1 = "." + b.opt.ConflictSuffix1 + b.opt.ConflictSuffix2 = "." + b.opt.ConflictSuffix2 + + // checks and warnings + if (b.opt.ConflictResolve == PreferNewer || b.opt.ConflictResolve == PreferOlder) && (b.fs1.Precision() == fs.ModTimeNotSupported || b.fs2.Precision() == fs.ModTimeNotSupported) { + fs.Logf(nil, Color(terminal.YellowFg, "WARNING: ignoring --conflict-resolve %s as at least one remote does not support modtimes."), b.opt.ConflictResolve.String()) + b.opt.ConflictResolve = PreferNone + } else if (b.opt.ConflictResolve == PreferNewer || b.opt.ConflictResolve == PreferOlder) && !b.opt.Compare.Modtime { + fs.Logf(nil, Color(terminal.YellowFg, "WARNING: ignoring --conflict-resolve %s as --compare does not include modtime."), b.opt.ConflictResolve.String()) + b.opt.ConflictResolve = PreferNone + } + if (b.opt.ConflictResolve == PreferLarger || b.opt.ConflictResolve == PreferSmaller) && !b.opt.Compare.Size { + fs.Logf(nil, Color(terminal.YellowFg, "WARNING: ignoring --conflict-resolve %s as --compare does not include size."), b.opt.ConflictResolve.String()) + b.opt.ConflictResolve = PreferNone + } + + return nil +} + +type renames map[string]renamesInfo // [originalName]newName (remember the originalName may have an alias) +// the newName may be the same as the old name (if winner), but should not be blank, unless we're deleting. +// the oldNames may not match each other, if we're normalizing case or unicode +// all names should be "remotes" (relative names, without base path) +type renamesInfo struct { + path1 namePair + path2 namePair +} +type namePair struct { + oldName string + newName string +} + +func (b *bisyncRun) resolve(ctxMove context.Context, path1, path2, file, alias string, renameSkipped, copy1to2, copy2to1 *bilib.Names, ds1, ds2 *deltaSet) error { + winningPath := 0 + if b.opt.ConflictResolve != PreferNone { + winningPath = b.conflictWinner(ds1, ds2, file, alias) + if winningPath > 0 { + fs.Infof(file, Color(terminal.GreenFg, "The winner is: Path%d"), winningPath) + } else { + fs.Infof(file, Color(terminal.RedFg, "A winner could not be determined.")) + } + } + + suff1 := b.opt.ConflictSuffix1 // copy to new var to make sure our changes here don't persist + suff2 := b.opt.ConflictSuffix2 + if b.opt.ConflictLoser == ConflictLoserPathname && b.opt.ConflictSuffix1 == b.opt.ConflictSuffix2 { + // numerate, but not if user supplied two different suffixes + suff1 += "1" + suff2 += "2" + } + + r := renamesInfo{ + path1: namePair{ + oldName: file, + newName: SuffixName(ctxMove, file, suff1), + }, + path2: namePair{ + oldName: alias, + newName: SuffixName(ctxMove, alias, suff2), + }, + } + + // handle auto-numbering + // note that we still queue copies for both files, whether or not we renamed + // we also set these for ConflictLoserDelete in case there is no winner. + if b.opt.ConflictLoser == ConflictLoserNumber || b.opt.ConflictLoser == ConflictLoserDelete { + num := b.numerate(ctxMove, 1, file, alias) + switch winningPath { + case 1: // keep path1, rename path2 + r.path1.newName = r.path1.oldName + r.path2.newName = SuffixName(ctxMove, r.path2.oldName, b.opt.ConflictSuffix2+fmt.Sprint(num)) + case 2: // keep path2, rename path1 + r.path1.newName = SuffixName(ctxMove, r.path1.oldName, b.opt.ConflictSuffix1+fmt.Sprint(num)) + r.path2.newName = r.path2.oldName + default: // no winner, so rename both to different numbers (unless suffixes are already different) + if b.opt.ConflictSuffix1 == b.opt.ConflictSuffix2 { + r.path1.newName = SuffixName(ctxMove, r.path1.oldName, b.opt.ConflictSuffix1+fmt.Sprint(num)) + // let's just make sure num + 1 is available... + num2 := b.numerate(ctxMove, num+1, file, alias) + r.path2.newName = SuffixName(ctxMove, r.path2.oldName, b.opt.ConflictSuffix2+fmt.Sprint(num2)) + } else { + // suffixes are different, so numerate independently + num = b.numerateSingle(ctxMove, 1, file, alias, 1) + r.path1.newName = SuffixName(ctxMove, r.path1.oldName, b.opt.ConflictSuffix1+fmt.Sprint(num)) + num = b.numerateSingle(ctxMove, 1, file, alias, 2) + r.path2.newName = SuffixName(ctxMove, r.path2.oldName, b.opt.ConflictSuffix2+fmt.Sprint(num)) + } + } + } + + // when winningPath == 0 (no winner), we ignore settings and rename both, do not delete + // note also that deletes and renames are mutually exclusive -- we never delete one path and rename the other. + if b.opt.ConflictLoser == ConflictLoserDelete && winningPath == 1 { + // delete 2, copy 1 to 2 + err = b.delete(ctxMove, r.path2, path2, path1, b.fs2, 2, 1, renameSkipped) + if err != nil { + return err + } + r.path2.newName = "" + // copy the one that wasn't deleted + b.indent("Path1", r.path1.oldName, "Queue copy to Path2") + copy1to2.Add(r.path1.oldName) + } else if b.opt.ConflictLoser == ConflictLoserDelete && winningPath == 2 { + // delete 1, copy 2 to 1 + err = b.delete(ctxMove, r.path1, path1, path2, b.fs1, 1, 2, renameSkipped) + if err != nil { + return err + } + r.path1.newName = "" + // copy the one that wasn't deleted + b.indent("Path2", r.path2.oldName, "Queue copy to Path1") + copy2to1.Add(r.path2.oldName) + } else { + err = b.rename(ctxMove, r.path1, path1, path2, b.fs1, 1, 2, winningPath, copy1to2, renameSkipped) + if err != nil { + return err + } + err = b.rename(ctxMove, r.path2, path2, path1, b.fs2, 2, 1, winningPath, copy2to1, renameSkipped) + if err != nil { + return err + } + } + + b.renames[r.path1.oldName] = r // note map index is path1's oldName, which may be different from path2 if aliases + return nil +} + +// SuffixName adds the current --conflict-suffix to the remote, obeying +// --suffix-keep-extension if set +// It is a close cousin of operations.SuffixName, but we don't want to +// use ci.Suffix for this because it might be used for --backup-dir. +func SuffixName(ctx context.Context, remote, suffix string) string { + if suffix == "" { + return remote + } + ci := fs.GetConfig(ctx) + if ci.SuffixKeepExtension { + var ( + base = remote + exts = "" + first = true + ext = path.Ext(remote) + ) + for ext != "" { + // Look second and subsequent extensions in mime types. + // If they aren't found then don't keep it as an extension. + if !first && mime.TypeByExtension(ext) == "" { + break + } + base = base[:len(base)-len(ext)] + exts = ext + exts + first = false + ext = path.Ext(base) + } + return base + suffix + exts + } + return remote + suffix +} + +// NotEmpty checks whether set is not empty +func (r renames) NotEmpty() bool { + return len(r) > 0 +} + +func (ri *renamesInfo) getNames(is1to2 bool) (srcOldName, srcNewName, dstOldName, dstNewName string) { + if is1to2 { + return ri.path1.oldName, ri.path1.newName, ri.path2.oldName, ri.path2.newName + } + return ri.path2.oldName, ri.path2.newName, ri.path1.oldName, ri.path1.newName +} + +// work out the lowest number that niether side has, return it for suffix +func (b *bisyncRun) numerate(ctx context.Context, startnum int, file, alias string) int { + for i := startnum; i < math.MaxInt; i++ { + iStr := fmt.Sprint(i) + if !ls1.has(SuffixName(ctx, file, b.opt.ConflictSuffix1+iStr)) && + !ls1.has(SuffixName(ctx, alias, b.opt.ConflictSuffix1+iStr)) && + !ls2.has(SuffixName(ctx, file, b.opt.ConflictSuffix2+iStr)) && + !ls2.has(SuffixName(ctx, alias, b.opt.ConflictSuffix2+iStr)) { + // make sure it still holds true with suffixes switched (it should) + if !ls1.has(SuffixName(ctx, file, b.opt.ConflictSuffix2+iStr)) && + !ls1.has(SuffixName(ctx, alias, b.opt.ConflictSuffix2+iStr)) && + !ls2.has(SuffixName(ctx, file, b.opt.ConflictSuffix1+iStr)) && + !ls2.has(SuffixName(ctx, alias, b.opt.ConflictSuffix1+iStr)) { + fs.Debugf(file, "The first available suffix is: %s", iStr) + return i + } + } + } + return 0 // not really possible, as no one has 9223372036854775807 conflicts, and if they do, they have bigger problems +} + +// like numerate, but consider only one side's suffix (for when suffixes are different) +func (b *bisyncRun) numerateSingle(ctx context.Context, startnum int, file, alias string, path int) int { + lsA, lsB := ls1, ls2 + suffix := b.opt.ConflictSuffix1 + if path == 2 { + lsA, lsB = ls2, ls1 + suffix = b.opt.ConflictSuffix2 + } + for i := startnum; i < math.MaxInt; i++ { + iStr := fmt.Sprint(i) + if !lsA.has(SuffixName(ctx, file, suffix+iStr)) && + !lsA.has(SuffixName(ctx, alias, suffix+iStr)) && + !lsB.has(SuffixName(ctx, file, suffix+iStr)) && + !lsB.has(SuffixName(ctx, alias, suffix+iStr)) { + fs.Debugf(file, "The first available suffix is: %s", iStr) + return i + } + } + return 0 // not really possible, as no one has 9223372036854775807 conflicts, and if they do, they have bigger problems +} + +func (b *bisyncRun) rename(ctx context.Context, thisNamePair namePair, thisPath, thatPath string, thisFs fs.Fs, thisPathNum, thatPathNum, winningPath int, q, renameSkipped *bilib.Names) error { + if winningPath == thisPathNum { + b.indent(fmt.Sprintf("!Path%d", thisPathNum), thisPath+thisNamePair.newName, fmt.Sprintf("Not renaming Path%d copy, as it was determined the winner", thisPathNum)) + } else { + skip := operations.SkipDestructive(ctx, thisNamePair.oldName, "rename") + if !skip { + b.indent(fmt.Sprintf("!Path%d", thisPathNum), thisPath+thisNamePair.newName, fmt.Sprintf("Renaming Path%d copy", thisPathNum)) + ctx = b.setBackupDir(ctx, thisPathNum) // in case already a file with new name + if err = operations.MoveFile(ctx, thisFs, thisFs, thisNamePair.newName, thisNamePair.oldName); err != nil { + err = fmt.Errorf("%s rename failed for %s: %w", thisPath, thisPath+thisNamePair.oldName, err) + b.critical = true + return err + } + } else { + renameSkipped.Add(thisNamePair.oldName) // (due to dry-run, not equality) + } + } + b.indent(fmt.Sprintf("!Path%d", thisPathNum), thatPath+thisNamePair.newName, fmt.Sprintf("Queue copy to Path%d", thatPathNum)) + q.Add(thisNamePair.newName) + return nil +} + +func (b *bisyncRun) delete(ctx context.Context, thisNamePair namePair, thisPath, thatPath string, thisFs fs.Fs, thisPathNum, thatPathNum int, renameSkipped *bilib.Names) error { + skip := operations.SkipDestructive(ctx, thisNamePair.oldName, "delete") + if !skip { + b.indent(fmt.Sprintf("!Path%d", thisPathNum), thisPath+thisNamePair.oldName, fmt.Sprintf("Deleting Path%d copy", thisPathNum)) + ctx = b.setBackupDir(ctx, thisPathNum) + ci := fs.GetConfig(ctx) + var backupDir fs.Fs + if ci.BackupDir != "" { + backupDir, err = operations.BackupDir(ctx, thisFs, thisFs, thisNamePair.oldName) + if err != nil { + b.critical = true + return err + } + } + obj, err := thisFs.NewObject(ctx, thisNamePair.oldName) + if err != nil { + b.critical = true + return err + } + if err = operations.DeleteFileWithBackupDir(ctx, obj, backupDir); err != nil { + err = fmt.Errorf("%s delete failed for %s: %w", thisPath, thisPath+thisNamePair.oldName, err) + b.critical = true + return err + } + } else { + renameSkipped.Add(thisNamePair.oldName) // (due to dry-run, not equality) + } + return nil +} + +func (b *bisyncRun) conflictWinner(ds1, ds2 *deltaSet, remote1, remote2 string) int { + switch b.opt.ConflictResolve { + case PreferPath1: + return 1 + case PreferPath2: + return 2 + case PreferNewer, PreferOlder: + return b.resolveNewerOlder(ds1, ds2, remote1, remote2, b.opt.ConflictResolve) + case PreferLarger, PreferSmaller: + return b.resolveLargerSmaller(ds1, ds2, remote1, remote2, b.opt.ConflictResolve) + default: + return 0 + } +} + +// returns the winning path number, or 0 if winner can't be determined +func (b *bisyncRun) resolveNewerOlder(ds1, ds2 *deltaSet, remote1, remote2 string, prefer Prefer) int { + if fs.GetModifyWindow(b.octx, b.fs1, b.fs2) == fs.ModTimeNotSupported { + fs.Infof(remote1, "Winner cannot be determined as at least one path lacks modtime support.") + return 0 + } + t1, t2 := ds1.time[remote1], ds2.time[remote2] + if t1.IsZero() || t2.IsZero() { + fs.Infof(remote1, "Winner cannot be determined as at least one modtime is missing. Path1: %v, Path2: %v", t1, t2) + return 0 + } + if t1.After(t2) { + if prefer == PreferNewer { + fs.Infof(remote1, "Path1 is newer. Path1: %v, Path2: %v, Difference: %s", t1, t2, t1.Sub(t2)) + return 1 + } else if prefer == PreferOlder { + fs.Infof(remote1, "Path2 is older. Path1: %v, Path2: %v, Difference: %s", t1, t2, t1.Sub(t2)) + return 2 + } + } else if t1.Before(t2) { + if prefer == PreferNewer { + fs.Infof(remote1, "Path2 is newer. Path1: %v, Path2: %v, Difference: %s", t1, t2, t2.Sub(t1)) + return 2 + } else if prefer == PreferOlder { + fs.Infof(remote1, "Path1 is older. Path1: %v, Path2: %v, Difference: %s", t1, t2, t2.Sub(t1)) + return 1 + } + } + if t1.Equal(t2) { + fs.Infof(remote1, "Winner cannot be determined as times are equal. Path1: %v, Path2: %v, Difference: %s", t1, t2, t2.Sub(t1)) + return 0 + } + fs.Errorf(remote1, "Winner cannot be determined. Path1: %v, Path2: %v", t1, t2) // shouldn't happen unless prefer is of wrong type + return 0 +} + +// returns the winning path number, or 0 if winner can't be determined +func (b *bisyncRun) resolveLargerSmaller(ds1, ds2 *deltaSet, remote1, remote2 string, prefer Prefer) int { + s1, s2 := ds1.size[remote1], ds2.size[remote2] + if s1 < 0 || s2 < 0 { + fs.Infof(remote1, "Winner cannot be determined as at least one size is unknown. Path1: %v, Path2: %v", s1, s2) + return 0 + } + if s1 > s2 { + if prefer == PreferLarger { + fs.Infof(remote1, "Path1 is larger. Path1: %v, Path2: %v, Difference: %v", s1, s2, s1-s2) + return 1 + } else if prefer == PreferSmaller { + fs.Infof(remote1, "Path2 is smaller. Path1: %v, Path2: %v, Difference: %v", s1, s2, s1-s2) + return 2 + } + } else if s1 < s2 { + if prefer == PreferLarger { + fs.Infof(remote1, "Path2 is larger. Path1: %v, Path2: %v, Difference: %v", s1, s2, s2-s1) + return 2 + } else if prefer == PreferSmaller { + fs.Infof(remote1, "Path1 is smaller. Path1: %v, Path2: %v, Difference: %v", s1, s2, s2-s1) + return 1 + } + } + if s1 == s2 { + fs.Infof(remote1, "Winner cannot be determined as sizes are equal. Path1: %v, Path2: %v, Difference: %v", s1, s2, s1-s2) + return 0 + } + fs.Errorf(remote1, "Winner cannot be determined. Path1: %v, Path2: %v", s1, s2) // shouldn't happen unless prefer is of wrong type + return 0 +} diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy1to2.que index 5725b8184..74c4ff68a 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1,5 +1,5 @@ "file11.txt" "file2.txt" "file4.txt" -"file5.txt..path1" +"file5.txt.conflict1" "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy2to1.que index 3b79e78bd..8097bfd9d 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -1,5 +1,5 @@ "file1.txt" "file10.txt" "file3.txt" -"file5.txt..path2" +"file5.txt.conflict2" "file6.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-err b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-err index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-err +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-err @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-new b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-new index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-new +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-new @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-old b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-old index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-old +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path1.lst-old @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-err b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-err index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-err +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-err @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-new b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-new index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-new +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-new @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-old b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-old index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-old +++ b/cmd/bisync/testdata/test_backupdir/golden/_testdir_path1.._testdir_path2.path2.lst-old @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_backupdir/golden/test.log b/cmd/bisync/testdata/test_backupdir/golden/test.log index ab8170d50..e398af723 100644 --- a/cmd/bisync/testdata/test_backupdir/golden/test.log +++ b/cmd/bisync/testdata/test_backupdir/golden/test.log @@ -101,10 +101,10 @@ INFO : - Path1 Queue copy to Path2 - { INFO : - Path1 Queue copy to Path2 - {path2/}file2.txt INFO : - Path2 Queue delete - {path2/}file4.txt NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict2 INFO : - Path2 Queue copy to Path1 - {path1/}file6.txt INFO : - Path1 Queue copy to Path2 - {path2/}file7.txt INFO : - Path2 Queue copy to Path1 - {path1/}file1.txt diff --git a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy1to2.que index 5725b8184..74c4ff68a 100644 --- a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1,5 +1,5 @@ "file11.txt" "file2.txt" "file4.txt" -"file5.txt..path1" +"file5.txt.conflict1" "file7.txt" diff --git a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy2to1.que index 3b79e78bd..8097bfd9d 100644 --- a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -1,5 +1,5 @@ "file1.txt" "file10.txt" "file3.txt" -"file5.txt..path2" +"file5.txt.conflict2" "file6.txt" diff --git a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path1.lst index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path1.lst +++ b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path1.lst @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path2.lst index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path2.lst +++ b/cmd/bisync/testdata/test_changes/golden/_testdir_path1.._testdir_path2.path2.lst @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_changes/golden/test.log b/cmd/bisync/testdata/test_changes/golden/test.log index 3d678b67a..535042521 100644 --- a/cmd/bisync/testdata/test_changes/golden/test.log +++ b/cmd/bisync/testdata/test_changes/golden/test.log @@ -101,10 +101,10 @@ INFO : - Path1 Queue copy to Path2 - { INFO : - Path1 Queue copy to Path2 - {path2/}file2.txt INFO : - Path2 Queue delete - {path2/}file4.txt NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict2 INFO : - Path2 Queue copy to Path1 - {path1/}file6.txt INFO : - Path1 Queue copy to Path2 - {path2/}file7.txt INFO : - Path2 Queue copy to Path1 - {path1/}file1.txt diff --git a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy1to2.que index 5725b8184..74c4ff68a 100644 --- a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1,5 +1,5 @@ "file11.txt" "file2.txt" "file4.txt" -"file5.txt..path1" +"file5.txt.conflict1" "file7.txt" diff --git a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy2to1.que index 3b79e78bd..8097bfd9d 100644 --- a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -1,5 +1,5 @@ "file1.txt" "file10.txt" "file3.txt" -"file5.txt..path2" +"file5.txt.conflict2" "file6.txt" diff --git a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path1.lst index ddb01f4a7..c691a789b 100644 --- a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path1.lst +++ b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path1.lst @@ -4,7 +4,7 @@ - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 md5:fb3ecfb2800400fb01b0bfd39903e9fb - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 md5:0860a03592626642f8fd6c8bfb447d2a - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 md5:979a803b15d27df0c31ad7d29006d10b - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 md5:0860a03592626642f8fd6c8bfb447d2a - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 md5:979a803b15d27df0c31ad7d29006d10b - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path2.lst index ddb01f4a7..c691a789b 100644 --- a/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path2.lst +++ b/cmd/bisync/testdata/test_compare_all/golden/_testdir_path1.._testdir_path2.path2.lst @@ -4,7 +4,7 @@ - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 md5:fb3ecfb2800400fb01b0bfd39903e9fb - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 md5:0860a03592626642f8fd6c8bfb447d2a - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 md5:979a803b15d27df0c31ad7d29006d10b - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 md5:0860a03592626642f8fd6c8bfb447d2a - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 md5:979a803b15d27df0c31ad7d29006d10b - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 md5:7fe98ed88552b828777d8630900346b8 - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_compare_all/golden/test.log b/cmd/bisync/testdata/test_compare_all/golden/test.log index fc2674d79..760cb69f6 100644 --- a/cmd/bisync/testdata/test_compare_all/golden/test.log +++ b/cmd/bisync/testdata/test_compare_all/golden/test.log @@ -94,10 +94,10 @@ INFO : - Path1 Queue copy to Path2 - { INFO : - Path1 Queue copy to Path2 - {path2/}file2.txt INFO : - Path2 Queue delete - {path2/}file4.txt NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict2 INFO : - Path2 Queue copy to Path1 - {path1/}file6.txt INFO : - Path1 Queue copy to Path2 - {path2/}file7.txt INFO : - Path2 Queue copy to Path1 - {path1/}file1.txt diff --git a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy1to2.que index 5725b8184..74c4ff68a 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1,5 +1,5 @@ "file11.txt" "file2.txt" "file4.txt" -"file5.txt..path1" +"file5.txt.conflict1" "file7.txt" diff --git a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy2to1.que index 3b79e78bd..8097bfd9d 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -1,5 +1,5 @@ "file1.txt" "file10.txt" "file3.txt" -"file5.txt..path2" +"file5.txt.conflict2" "file6.txt" diff --git a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path1.lst index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path1.lst +++ b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path1.lst @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path2.lst index 19e9623a5..28bcb7af7 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path2.lst +++ b/cmd/bisync/testdata/test_dry_run/golden/_testdir_path1.._testdir_path2.path2.lst @@ -4,7 +4,7 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "file10.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file11.txt" - 13 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file6.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "file7.txt" diff --git a/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy1to2.que index 5725b8184..74c4ff68a 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy1to2.que @@ -1,5 +1,5 @@ "file11.txt" "file2.txt" "file4.txt" -"file5.txt..path1" +"file5.txt.conflict1" "file7.txt" diff --git a/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy2to1.que index 3b79e78bd..8097bfd9d 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_dry_run/golden/dryrun._testdir_path1.._testdir_path2.copy2to1.que @@ -1,5 +1,5 @@ "file1.txt" "file10.txt" "file3.txt" -"file5.txt..path2" +"file5.txt.conflict2" "file6.txt" diff --git a/cmd/bisync/testdata/test_dry_run/golden/test.log b/cmd/bisync/testdata/test_dry_run/golden/test.log index 1736942d4..9b5f48d90 100644 --- a/cmd/bisync/testdata/test_dry_run/golden/test.log +++ b/cmd/bisync/testdata/test_dry_run/golden/test.log @@ -123,12 +123,10 @@ INFO : - Path1 Queue copy to Path2 - { INFO : - Path1 Queue copy to Path2 - {path2/}file2.txt INFO : - Path2 Queue delete - {path2/}file4.txt NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: file5.txt: Skipped move as --dry-run is set (size 39) -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: file5.txt: Skipped move as --dry-run is set (size 39) -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: file5.txt: Skipped rename as --dry-run is set +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict1 +NOTICE: file5.txt: Skipped rename as --dry-run is set +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict2 INFO : - Path2 Queue copy to Path1 - {path1/}file6.txt INFO : - Path1 Queue copy to Path2 - {path2/}file7.txt INFO : - Path2 Queue copy to Path1 - {path1/}file1.txt @@ -190,10 +188,10 @@ INFO : - Path1 Queue copy to Path2 - { INFO : - Path1 Queue copy to Path2 - {path2/}file2.txt INFO : - Path2 Queue delete - {path2/}file4.txt NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict2 INFO : - Path2 Queue copy to Path1 - {path1/}file6.txt INFO : - Path1 Queue copy to Path2 - {path2/}file7.txt INFO : - Path2 Queue copy to Path1 - {path1/}file1.txt diff --git a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy1to2.que index d988a5ab1..91974e009 100644 --- a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1 +1 @@ -"file1.txt..path1" +"file1.txt.conflict1" diff --git a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy2to1.que index 7f99cd1ce..172a192aa 100644 --- a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -1 +1 @@ -"file1.txt..path2" +"file1.txt.conflict2" diff --git a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path1.lst index 3e93d92de..2e181c871 100644 --- a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path1.lst +++ b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path1.lst @@ -1,5 +1,5 @@ # bisync listing v1 from test - 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" -- 33 - - 2001-03-04T00:00:00.000000000+0000 "file1.txt..path1" -- 33 - - 2001-01-02T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2001-03-04T00:00:00.000000000+0000 "file1.txt.conflict1" +- 33 - - 2001-01-02T00:00:00.000000000+0000 "file1.txt.conflict2" - 37 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" diff --git a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path2.lst index 3e93d92de..2e181c871 100644 --- a/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path2.lst +++ b/cmd/bisync/testdata/test_equal/golden/_testdir_path1.._testdir_path2.path2.lst @@ -1,5 +1,5 @@ # bisync listing v1 from test - 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" -- 33 - - 2001-03-04T00:00:00.000000000+0000 "file1.txt..path1" -- 33 - - 2001-01-02T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2001-03-04T00:00:00.000000000+0000 "file1.txt.conflict1" +- 33 - - 2001-01-02T00:00:00.000000000+0000 "file1.txt.conflict2" - 37 - - 2001-01-02T00:00:00.000000000+0000 "file2.txt" diff --git a/cmd/bisync/testdata/test_equal/golden/test.log b/cmd/bisync/testdata/test_equal/golden/test.log index e496bc95e..f52018acd 100644 --- a/cmd/bisync/testdata/test_equal/golden/test.log +++ b/cmd/bisync/testdata/test_equal/golden/test.log @@ -63,10 +63,10 @@ NOTICE: {path2String}: 1 errors while checking NOTICE: {path2String}: 1 matching files INFO : Finished checking the potential conflicts. 1 differences found NOTICE: - WARNING New or changed in both paths - file1.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file1.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file1.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file1.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file1.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file1.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file1.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file1.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file1.txt.conflict2 NOTICE: - WARNING New or changed in both paths - file2.txt INFO : Files are equal! Skipping: file2.txt INFO : - Path2 Do queued copies to - Path1 diff --git a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy1to2.que index 75c663f86..390061065 100644 --- a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1,6 +1,6 @@ "file1_with white space.txt" "filename_contains_ࢺ_p1m.txt" "subdir_with_ࢺ_/file_with_測試_.txt" -"subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path1" +"subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict1" "subdir_with_ࢺ_/mañana_funcionará.txt" "subdir_with_ࢺ_/test.txt" diff --git a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy2to1.que index 5c39d3955..cd4932019 100644 --- a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -3,7 +3,7 @@ "filename_contains_ࢺ_.txt" "subdir with␊white space.txt/file2 with␊white space.txt" "subdir_rawchars_␙_\x81_\xfe/file3_␙_\x81_\xfe" -"subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path2" +"subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict2" "subdir_with_ࢺ_/filename_contains_ě_.txt" "subdir_with_ࢺ_/filename_contains_ࢺ_p2s.txt" "Русский.txt" diff --git a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path1.lst index 348c62c3d..619c3a8a2 100644 --- a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path1.lst +++ b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path1.lst @@ -11,8 +11,8 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir with␊white space.txt/file2 with␊white space.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_rawchars_␙_\x81_\xfe/file3_␙_\x81_\xfe" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/file_with_測試_.txt" -- 19 - - 2001-01-03T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path1" -- 42 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path2" +- 19 - - 2001-01-03T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict1" +- 42 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict2" - 272 - - 2000-01-01T00:00:00.000000000+0000 "subdir_with_ࢺ_/filename_contains_ࢺ_.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/filename_contains_ࢺ_p2s.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/mañana_funcionará.txt" diff --git a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path2.lst index 348c62c3d..619c3a8a2 100644 --- a/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path2.lst +++ b/cmd/bisync/testdata/test_extended_filenames/golden/_testdir_path1.._testdir_path2.path2.lst @@ -11,8 +11,8 @@ - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir with␊white space.txt/file2 with␊white space.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_rawchars_␙_\x81_\xfe/file3_␙_\x81_\xfe" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/file_with_測試_.txt" -- 19 - - 2001-01-03T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path1" -- 42 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path2" +- 19 - - 2001-01-03T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict1" +- 42 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict2" - 272 - - 2000-01-01T00:00:00.000000000+0000 "subdir_with_ࢺ_/filename_contains_ࢺ_.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/filename_contains_ࢺ_p2s.txt" - 19 - - 2001-01-02T00:00:00.000000000+0000 "subdir_with_ࢺ_/mañana_funcionará.txt" diff --git a/cmd/bisync/testdata/test_extended_filenames/golden/test.log b/cmd/bisync/testdata/test_extended_filenames/golden/test.log index 5f5e31959..25bf9ea43 100644 --- a/cmd/bisync/testdata/test_extended_filenames/golden/test.log +++ b/cmd/bisync/testdata/test_extended_filenames/golden/test.log @@ -96,10 +96,10 @@ INFO : - Path1 Queue copy to Path2 - { INFO : - Path1 Queue copy to Path2 - {path2/}filename_contains_ࢺ_p1m.txt INFO : - Path1 Queue copy to Path2 - {path2/}subdir_with_ࢺ_/file_with_測試_.txt NOTICE: - WARNING New or changed in both paths - subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}subdir_with_ࢺ_/filechangedbothpaths_ࢺ_.txt.conflict2 INFO : - Path1 Queue copy to Path2 - {path2/}subdir_with_ࢺ_/mañana_funcionará.txt INFO : - Path1 Queue copy to Path2 - {path2/}subdir_with_ࢺ_/test.txt INFO : - Path2 Queue copy to Path1 - {path1/}Русский.txt diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy1to2.que new file mode 100644 index 000000000..029f3e190 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -0,0 +1 @@ +"file1.txt" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy2to1.que new file mode 100644 index 000000000..9ed593ba3 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -0,0 +1 @@ +"file2.txt" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst new file mode 100644 index 000000000..57872a027 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst @@ -0,0 +1,10 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 33 - - 2006-03-04T00:00:00.000000000+0000 "file1.txt" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path1" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2002-08-26T00:00:00.000000000+0000 "file1.txt.apple1" +- 33 - - 2002-07-23T00:00:00.000000000+0000 "file1.txt.apple2" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.cloud1" +- 33 - - 2001-08-26T00:00:00.000000000+0000 "file1.txt.dinosaur1" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.local1" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-new b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-new new file mode 100644 index 000000000..454d9344c --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-new @@ -0,0 +1,11 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 33 - - 2006-03-04T00:00:00.000000000+0000 "file1.txt" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path1" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2002-08-26T00:00:00.000000000+0000 "file1.txt.apple1" +- 33 - - 2002-07-23T00:00:00.000000000+0000 "file1.txt.apple2" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.cloud1" +- 33 - - 2001-08-26T00:00:00.000000000+0000 "file1.txt.dinosaur1" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.local1" +- 33 - - 2005-01-02T00:00:00.000000000+0000 "file2.txt" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-old b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-old new file mode 100644 index 000000000..2779be7ab --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path1.lst-old @@ -0,0 +1,10 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path1" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2002-08-26T00:00:00.000000000+0000 "file1.txt.apple1" +- 33 - - 2002-07-23T00:00:00.000000000+0000 "file1.txt.apple2" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.cloud1" +- 33 - - 2001-08-26T00:00:00.000000000+0000 "file1.txt.dinosaur1" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.local1" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst new file mode 100644 index 000000000..57872a027 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst @@ -0,0 +1,10 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 33 - - 2006-03-04T00:00:00.000000000+0000 "file1.txt" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path1" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2002-08-26T00:00:00.000000000+0000 "file1.txt.apple1" +- 33 - - 2002-07-23T00:00:00.000000000+0000 "file1.txt.apple2" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.cloud1" +- 33 - - 2001-08-26T00:00:00.000000000+0000 "file1.txt.dinosaur1" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.local1" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-new b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-new new file mode 100644 index 000000000..8b02b6d6a --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-new @@ -0,0 +1,11 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 33 - - 2005-01-02T00:00:00.000000000+0000 "file1.txt" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path1" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2002-08-26T00:00:00.000000000+0000 "file1.txt.apple1" +- 33 - - 2002-07-23T00:00:00.000000000+0000 "file1.txt.apple2" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.cloud1" +- 33 - - 2001-08-26T00:00:00.000000000+0000 "file1.txt.dinosaur1" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.local1" +- 33 - - 2006-03-04T00:00:00.000000000+0000 "file2.txt" diff --git a/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-old b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-old new file mode 100644 index 000000000..2779be7ab --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/_testdir_path1.._testdir_path2.path2.lst-old @@ -0,0 +1,10 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path1" +- 33 - - 2004-07-23T00:00:00.000000000+0000 "file1.txt..path2" +- 33 - - 2002-08-26T00:00:00.000000000+0000 "file1.txt.apple1" +- 33 - - 2002-07-23T00:00:00.000000000+0000 "file1.txt.apple2" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.cloud1" +- 33 - - 2001-08-26T00:00:00.000000000+0000 "file1.txt.dinosaur1" +- 33 - - 2003-07-23T00:00:00.000000000+0000 "file1.txt.local1" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" diff --git a/cmd/bisync/testdata/test_resolve/golden/test.log b/cmd/bisync/testdata/test_resolve/golden/test.log new file mode 100644 index 000000000..a8e129259 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/golden/test.log @@ -0,0 +1,304 @@ +(01) : test resolve + + +(02) : test initial bisync +(03) : bisync resync +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Copying unique Path2 files to Path1 +INFO : - Path2 Resync is copying UNIQUE files to - Path1 +INFO : - Path1 Resync is copying UNIQUE OR DIFFERING files to - Path2 +INFO : Resync updating listings +INFO : Bisync successful + +(04) : test changed on both paths and NOT identical - file1 (file1R, file1L) +(05) : touch-glob 2001-01-02 {datadir/} file1R.txt +(06) : copy-as {datadir/}file1R.txt {path2/} file1.txt +(07) : touch-glob 2001-03-04 {datadir/} file1L.txt +(08) : copy-as {datadir/}file1L.txt {path1/} file1.txt + +(09) : test bisync run with --conflict-resolve=newer --conflict-loser=delete --conflict-suffix=potato + +(10) : bisync conflict-resolve=newer conflict-loser=delete conflict-suffix=potato +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Building Path1 and Path2 listings +INFO : Path1 checking for diffs +INFO : - Path1 File changed: size (larger), time (newer) - file1.txt +INFO : Path1: 1 changes:  0 new,  1 modified,  0 deleted +INFO : (Modified:  1 newer,  0 older,  1 larger,  0 smaller) +INFO : Path2 checking for diffs +INFO : - Path2 File changed: size (larger), time (newer) - file1.txt +INFO : Path2: 1 changes:  0 new,  1 modified,  0 deleted +INFO : (Modified:  1 newer,  0 older,  1 larger,  0 smaller) +INFO : Applying changes +INFO : Checking potential conflicts... +ERROR : file1.txt: md5 differ +NOTICE: {path2String}: 1 differences found +NOTICE: {path2String}: 1 errors while checking +INFO : Finished checking the potential conflicts. 1 differences found +NOTICE: - WARNING New or changed in both paths - file1.txt +INFO : file1.txt: Path1 is newer. Path1: 2001-03-04 00:00:00 +0000 UTC, Path2: 2001-01-02 00:00:00 +0000 UTC, Difference: 1464h0m0s +INFO : file1.txt: The winner is: Path1 +NOTICE: - Path2 Deleting Path2 copy - {path2/}file1.txt +INFO : - Path1 Queue copy to Path2 - file1.txt +INFO : - Path1 Do queued copies to - Path2 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(11) : test changed on both paths and NOT identical - file1 (file1R, file1L) +(12) : touch-glob 2001-07-23 {datadir/} file1R.txt +(13) : copy-as {datadir/}file1R.txt {path2/} file1.txt +(14) : touch-glob 2001-08-26 {datadir/} file1L.txt +(15) : copy-as {datadir/}file1L.txt {path1/} file1.txt + +(16) : test bisync run with --conflict-resolve=path2 --conflict-loser=num --conflict-suffix=dinosaur + +(17) : bisync conflict-resolve=path2 conflict-loser=num conflict-suffix=dinosaur +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Building Path1 and Path2 listings +INFO : Path1 checking for diffs +INFO : - Path1 File changed: time (newer) - file1.txt +INFO : Path1: 1 changes:  0 new,  1 modified,  0 deleted +INFO : (Modified:  1 newer,  0 older) +INFO : Path2 checking for diffs +INFO : - Path2 File changed: time (newer) - file1.txt +INFO : Path2: 1 changes:  0 new,  1 modified,  0 deleted +INFO : (Modified:  1 newer,  0 older) +INFO : Applying changes +INFO : Checking potential conflicts... +ERROR : file1.txt: md5 differ +NOTICE: {path2String}: 1 differences found +NOTICE: {path2String}: 1 errors while checking +INFO : Finished checking the potential conflicts. 1 differences found +NOTICE: - WARNING New or changed in both paths - file1.txt +INFO : file1.txt: The winner is: Path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file1.txt.dinosaur1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file1.txt.dinosaur1 +NOTICE: - Path2 Not renaming Path2 copy, as it was determined the winner - {path2/}file1.txt +NOTICE: - Path2 Queue copy to Path1 - {path1/}file1.txt +INFO : - Path2 Do queued copies to - Path1 +INFO : - Path1 Do queued copies to - Path2 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(18) : test changed on both paths and NOT identical - file1 (file1R, file1L) +(19) : touch-glob 2002-07-23 {datadir/} file1R.txt +(20) : copy-as {datadir/}file1R.txt {path2/} file1.txt +(21) : touch-glob 2002-08-26 {datadir/} file1L.txt +(22) : copy-as {datadir/}file1L.txt {path1/} file1.txt + +(23) : test bisync run with --conflict-resolve=larger --conflict-loser=num --conflict-suffix=apple + +(24) : bisync conflict-resolve=larger conflict-loser=num conflict-suffix=apple +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Building Path1 and Path2 listings +INFO : Path1 checking for diffs +INFO : - Path1 File is new - file1.txt +INFO : Path1: 1 changes:  1 new,  0 modified,  0 deleted +INFO : Path2 checking for diffs +INFO : - Path2 File is new - file1.txt +INFO : Path2: 1 changes:  1 new,  0 modified,  0 deleted +INFO : Applying changes +INFO : Checking potential conflicts... +ERROR : file1.txt: md5 differ +NOTICE: {path2String}: 1 differences found +NOTICE: {path2String}: 1 errors while checking +INFO : Finished checking the potential conflicts. 1 differences found +NOTICE: - WARNING New or changed in both paths - file1.txt +INFO : file1.txt: Winner cannot be determined as sizes are equal. Path1: 33, Path2: 33, Difference: 0 +INFO : file1.txt: A winner could not be determined. +NOTICE: - Path1 Renaming Path1 copy - {path1/}file1.txt.apple1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file1.txt.apple1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file1.txt.apple2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file1.txt.apple2 +INFO : - Path2 Do queued copies to - Path1 +INFO : - Path1 Do queued copies to - Path2 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(25) : test different suffixes +(26) : touch-glob 2003-07-23 {datadir/} file1R.txt +(27) : copy-as {datadir/}file1R.txt {path2/} file1.txt +(28) : touch-glob 2003-07-23 {datadir/} file1L.txt +(29) : copy-as {datadir/}file1L.txt {path1/} file1.txt + +(30) : test bisync run with --conflict-resolve=older --conflict-loser=num --conflict-suffix=cloud,local + +(31) : bisync conflict-resolve=older conflict-loser=num conflict-suffix=cloud,local +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Building Path1 and Path2 listings +INFO : Path1 checking for diffs +INFO : - Path1 File is new - file1.txt +INFO : Path1: 1 changes:  1 new,  0 modified,  0 deleted +INFO : Path2 checking for diffs +INFO : - Path2 File is new - file1.txt +INFO : Path2: 1 changes:  1 new,  0 modified,  0 deleted +INFO : Applying changes +INFO : Checking potential conflicts... +ERROR : file1.txt: md5 differ +NOTICE: {path2String}: 1 differences found +NOTICE: {path2String}: 1 errors while checking +INFO : Finished checking the potential conflicts. 1 differences found +NOTICE: - WARNING New or changed in both paths - file1.txt +INFO : file1.txt: Winner cannot be determined as times are equal. Path1: 2003-07-23 00:00:00 +0000 UTC, Path2: 2003-07-23 00:00:00 +0000 UTC, Difference: 0s +INFO : file1.txt: A winner could not be determined. +NOTICE: - Path1 Renaming Path1 copy - {path1/}file1.txt.cloud1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file1.txt.cloud1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file1.txt.local1 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file1.txt.local1 +INFO : - Path2 Do queued copies to - Path1 +INFO : - Path1 Do queued copies to - Path2 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(32) : test legacy +(33) : touch-glob 2004-07-23 {datadir/} file1R.txt +(34) : copy-as {datadir/}file1R.txt {path2/} file1.txt +(35) : touch-glob 2004-07-23 {datadir/} file1L.txt +(36) : copy-as {datadir/}file1L.txt {path1/} file1.txt + +(37) : test bisync run with --conflict-resolve=none --conflict-loser=pathname --conflict-suffix=.path + +(38) : bisync conflict-resolve=none conflict-loser=pathname conflict-suffix=.path +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Building Path1 and Path2 listings +INFO : Path1 checking for diffs +INFO : - Path1 File is new - file1.txt +INFO : Path1: 1 changes:  1 new,  0 modified,  0 deleted +INFO : Path2 checking for diffs +INFO : - Path2 File is new - file1.txt +INFO : Path2: 1 changes:  1 new,  0 modified,  0 deleted +INFO : Applying changes +INFO : Checking potential conflicts... +ERROR : file1.txt: md5 differ +NOTICE: {path2String}: 1 differences found +NOTICE: {path2String}: 1 errors while checking +INFO : Finished checking the potential conflicts. 1 differences found +NOTICE: - WARNING New or changed in both paths - file1.txt +NOTICE: - Path1 Renaming Path1 copy - {path1/}file1.txt..path1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file1.txt..path1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file1.txt..path2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file1.txt..path2 +INFO : - Path2 Do queued copies to - Path1 +INFO : - Path1 Do queued copies to - Path2 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(39) : test deletes on both sides with default suffix +(40) : touch-glob 2005-01-02 {datadir/} file1R.txt +(41) : copy-as {datadir/}file1R.txt {path2/} file1.txt +(42) : copy-as {datadir/}file1R.txt {path1/} file2.txt +(43) : touch-glob 2006-03-04 {datadir/} file1L.txt +(44) : copy-as {datadir/}file1L.txt {path1/} file1.txt +(45) : copy-as {datadir/}file1L.txt {path2/} file2.txt + +(46) : test bisync run with --conflict-resolve=newer --conflict-loser=delete +(47) : bisync conflict-resolve=newer conflict-loser=delete +INFO : Setting --ignore-listing-checksum as neither --checksum nor --compare checksum are set. +INFO : Bisyncing with Comparison Settings: +{ +"Modtime": true, +"Size": true, +"Checksum": false, +"NoSlowHash": false, +"SlowHashSyncOnly": false, +"DownloadHash": false +} +INFO : Synching Path1 "{path1/}" with Path2 "{path2/}" +INFO : Building Path1 and Path2 listings +INFO : Path1 checking for diffs +INFO : - Path1 File changed: size (larger), time (newer) - file2.txt +INFO : - Path1 File is new - file1.txt +INFO : Path1: 2 changes:  1 new,  1 modified,  0 deleted +INFO : (Modified:  1 newer,  0 older,  1 larger,  0 smaller) +INFO : Path2 checking for diffs +INFO : - Path2 File changed: size (larger), time (newer) - file2.txt +INFO : - Path2 File is new - file1.txt +INFO : Path2: 2 changes:  1 new,  1 modified,  0 deleted +INFO : (Modified:  1 newer,  0 older,  1 larger,  0 smaller) +INFO : Applying changes +INFO : Checking potential conflicts... +ERROR : file1.txt: md5 differ +ERROR : file2.txt: md5 differ +NOTICE: {path2String}: 2 differences found +NOTICE: {path2String}: 2 errors while checking +INFO : Finished checking the potential conflicts. 2 differences found +NOTICE: - WARNING New or changed in both paths - file1.txt +INFO : file1.txt: Path1 is newer. Path1: 2006-03-04 00:00:00 +0000 UTC, Path2: 2005-01-02 00:00:00 +0000 UTC, Difference: 10224h0m0s +INFO : file1.txt: The winner is: Path1 +NOTICE: - Path2 Deleting Path2 copy - {path2/}file1.txt +INFO : - Path1 Queue copy to Path2 - file1.txt +NOTICE: - WARNING New or changed in both paths - file2.txt +INFO : file2.txt: Path2 is newer. Path1: 2005-01-02 00:00:00 +0000 UTC, Path2: 2006-03-04 00:00:00 +0000 UTC, Difference: 10224h0m0s +INFO : file2.txt: The winner is: Path2 +NOTICE: - Path1 Deleting Path1 copy - {path1/}file2.txt +INFO : - Path2 Queue copy to Path1 - file2.txt +INFO : - Path2 Do queued copies to - Path1 +INFO : - Path1 Do queued copies to - Path2 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful diff --git a/cmd/bisync/testdata/test_resolve/initial/RCLONE_TEST b/cmd/bisync/testdata/test_resolve/initial/RCLONE_TEST new file mode 100644 index 000000000..d8ca97c2a --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/initial/RCLONE_TEST @@ -0,0 +1 @@ +This file is used for testing the health of rclone accesses to the local/remote file system. Do not delete. diff --git a/cmd/bisync/testdata/test_resolve/initial/file1.txt b/cmd/bisync/testdata/test_resolve/initial/file1.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_resolve/initial/file2.txt b/cmd/bisync/testdata/test_resolve/initial/file2.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_resolve/modfiles/file1L.txt b/cmd/bisync/testdata/test_resolve/modfiles/file1L.txt new file mode 100644 index 000000000..6906ccb09 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/modfiles/file1L.txt @@ -0,0 +1 @@ +This file is NOT identical to 1R diff --git a/cmd/bisync/testdata/test_resolve/modfiles/file1R.txt b/cmd/bisync/testdata/test_resolve/modfiles/file1R.txt new file mode 100644 index 000000000..e49d06a94 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/modfiles/file1R.txt @@ -0,0 +1 @@ +This file is NOT identical to 1L diff --git a/cmd/bisync/testdata/test_resolve/modfiles/file2.txt b/cmd/bisync/testdata/test_resolve/modfiles/file2.txt new file mode 100644 index 000000000..2ee5fc1ee --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/modfiles/file2.txt @@ -0,0 +1 @@ +This file is identical on both sides diff --git a/cmd/bisync/testdata/test_resolve/scenario.txt b/cmd/bisync/testdata/test_resolve/scenario.txt new file mode 100644 index 000000000..1e72757f7 --- /dev/null +++ b/cmd/bisync/testdata/test_resolve/scenario.txt @@ -0,0 +1,67 @@ +test resolve +# Check conflict-resolution options +# - Changed on Path2 and on Path1, and NOT identical file1 (file1r, file1l) + +test initial bisync +bisync resync + +test changed on both paths and NOT identical - file1 (file1R, file1L) +touch-glob 2001-01-02 {datadir/} file1R.txt +copy-as {datadir/}file1R.txt {path2/} file1.txt +touch-glob 2001-03-04 {datadir/} file1L.txt +copy-as {datadir/}file1L.txt {path1/} file1.txt + +test bisync run with --conflict-resolve=newer --conflict-loser=delete --conflict-suffix=potato +# result should be "file1.txt", "file1.txt" (no potato) +bisync conflict-resolve=newer conflict-loser=delete conflict-suffix=potato + +test changed on both paths and NOT identical - file1 (file1R, file1L) +touch-glob 2001-07-23 {datadir/} file1R.txt +copy-as {datadir/}file1R.txt {path2/} file1.txt +touch-glob 2001-08-26 {datadir/} file1L.txt +copy-as {datadir/}file1L.txt {path1/} file1.txt + +test bisync run with --conflict-resolve=path2 --conflict-loser=num --conflict-suffix=dinosaur +# result should be "file1.txt.dinosaur1", "file1.txt" +bisync conflict-resolve=path2 conflict-loser=num conflict-suffix=dinosaur + +test changed on both paths and NOT identical - file1 (file1R, file1L) +touch-glob 2002-07-23 {datadir/} file1R.txt +copy-as {datadir/}file1R.txt {path2/} file1.txt +touch-glob 2002-08-26 {datadir/} file1L.txt +copy-as {datadir/}file1L.txt {path1/} file1.txt + +test bisync run with --conflict-resolve=larger --conflict-loser=num --conflict-suffix=apple +# result should be no winner -- "file1.txt.apple1", "file1.txt.apple2" +bisync conflict-resolve=larger conflict-loser=num conflict-suffix=apple + +test different suffixes +touch-glob 2003-07-23 {datadir/} file1R.txt +copy-as {datadir/}file1R.txt {path2/} file1.txt +touch-glob 2003-07-23 {datadir/} file1L.txt +copy-as {datadir/}file1L.txt {path1/} file1.txt + +test bisync run with --conflict-resolve=older --conflict-loser=num --conflict-suffix=cloud,local +# result should be no winner -- "file1.txt.cloud1", "file1.txt.local1" +bisync conflict-resolve=older conflict-loser=num conflict-suffix=cloud,local + +test legacy +touch-glob 2004-07-23 {datadir/} file1R.txt +copy-as {datadir/}file1R.txt {path2/} file1.txt +touch-glob 2004-07-23 {datadir/} file1L.txt +copy-as {datadir/}file1L.txt {path1/} file1.txt + +test bisync run with --conflict-resolve=none --conflict-loser=pathname --conflict-suffix=.path +# result should be no winner -- "file1.txt..path1", "file1.txt..path2" +bisync conflict-resolve=none conflict-loser=pathname conflict-suffix=.path + +test deletes on both sides with default suffix +touch-glob 2005-01-02 {datadir/} file1R.txt +copy-as {datadir/}file1R.txt {path2/} file1.txt +copy-as {datadir/}file1R.txt {path1/} file2.txt +touch-glob 2006-03-04 {datadir/} file1L.txt +copy-as {datadir/}file1L.txt {path1/} file1.txt +copy-as {datadir/}file1L.txt {path2/} file2.txt + +test bisync run with --conflict-resolve=newer --conflict-loser=delete +bisync conflict-resolve=newer conflict-loser=delete \ No newline at end of file diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy1to2.que b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy1to2.que index cc3b08914..185e98b18 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy1to2.que +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy1to2.que @@ -1 +1 @@ -"file5.txt..path1" +"file5.txt.conflict5" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy2to1.que index 8457cd213..68562bb61 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy2to1.que +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -1 +1 @@ -"file5.txt..path2" +"file5.txt.conflict6" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst index 1a9f106fc..5c8d754bb 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst @@ -46,8 +46,12 @@ - 19 - - 2023-08-26T00:00:00.000000000+0000 "file47.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file48.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file49.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict3" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict4" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict5" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict6" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file51.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file52.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file53.txt" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-new b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-new index 9adf42243..18bfbb3d0 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-new +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-new @@ -47,8 +47,10 @@ - 19 - - 2023-08-26T00:00:00.000000000+0000 "file48.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file49.txt" - 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict3" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict4" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file51.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file52.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file53.txt" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-old b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-old index 1a9f106fc..8190c0300 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-old +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path1.lst-old @@ -46,8 +46,10 @@ - 19 - - 2023-08-26T00:00:00.000000000+0000 "file47.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file48.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file49.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict3" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict4" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file51.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file52.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file53.txt" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst index 1a9f106fc..5c8d754bb 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst @@ -46,8 +46,12 @@ - 19 - - 2023-08-26T00:00:00.000000000+0000 "file47.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file48.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file49.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict3" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict4" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict5" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict6" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file51.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file52.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file53.txt" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-new b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-new index a8ce37fdc..968eeeae6 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-new +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-new @@ -47,8 +47,10 @@ - 19 - - 2023-08-26T00:00:00.000000000+0000 "file48.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file49.txt" - 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict3" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict4" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file51.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file52.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file53.txt" diff --git a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-old b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-old index 1a9f106fc..8190c0300 100644 --- a/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-old +++ b/cmd/bisync/testdata/test_volatile/golden/_testdir_path1.._testdir_path2.path2.lst-old @@ -46,8 +46,10 @@ - 19 - - 2023-08-26T00:00:00.000000000+0000 "file47.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file48.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file49.txt" -- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt..path1" -- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt..path2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict1" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict2" +- 39 - - 2001-03-04T00:00:00.000000000+0000 "file5.txt.conflict3" +- 39 - - 2001-01-02T00:00:00.000000000+0000 "file5.txt.conflict4" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file51.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file52.txt" - 19 - - 2023-08-26T00:00:00.000000000+0000 "file53.txt" diff --git a/cmd/bisync/testdata/test_volatile/golden/test.log b/cmd/bisync/testdata/test_volatile/golden/test.log index 7facb5980..d8af65a78 100644 --- a/cmd/bisync/testdata/test_volatile/golden/test.log +++ b/cmd/bisync/testdata/test_volatile/golden/test.log @@ -56,10 +56,10 @@ NOTICE: {path2String}: 1 differences found NOTICE: {path2String}: 1 errors while checking INFO : Finished checking the potential conflicts. 1 differences found NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict1 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict1 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict2 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict2 INFO : - Path2 Do queued copies to - Path1 INFO : - Path1 Do queued copies to - Path2 INFO : Updating listings @@ -201,10 +201,10 @@ NOTICE: {path2String}: 1 errors while checking INFO : Finished checking the potential conflicts. 1 differences found INFO : - Path1 Queue copy to Path2 - {path2/}file100.txt NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict3 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict3 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict4 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict4 INFO : - Path1 Queue copy to Path2 - {path2/}file51.txt INFO : - Path1 Queue copy to Path2 - {path2/}file52.txt INFO : - Path1 Queue copy to Path2 - {path2/}file53.txt @@ -343,10 +343,10 @@ NOTICE: {path2String}: 1 differences found NOTICE: {path2String}: 1 errors while checking INFO : Finished checking the potential conflicts. 1 differences found NOTICE: - WARNING New or changed in both paths - file5.txt -NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt..path1 -NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt..path1 -NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt..path2 -NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt..path2 +NOTICE: - Path1 Renaming Path1 copy - {path1/}file5.txt.conflict5 +NOTICE: - Path1 Queue copy to Path2 - {path2/}file5.txt.conflict5 +NOTICE: - Path2 Renaming Path2 copy - {path2/}file5.txt.conflict6 +NOTICE: - Path2 Queue copy to Path1 - {path1/}file5.txt.conflict6 INFO : - Path2 Do queued copies to - Path1 INFO : - Path1 Do queued copies to - Path2 INFO : Updating listings diff --git a/docs/content/bisync.md b/docs/content/bisync.md index 84b4a8735..a4ac4772c 100644 --- a/docs/content/bisync.md +++ b/docs/content/bisync.md @@ -422,6 +422,152 @@ of the current filters file and compares it to the hash stored in the `.md5` fil If they don't match, the run aborts with a critical error and thus forces you to do a `--resync`, likely avoiding a disaster. +### --conflict-resolve CHOICE {#conflict-resolve} + +In bisync, a "conflict" is a file that is *new* or *changed* on *both sides* +(relative to the prior run) AND is *not currently identical* on both sides. +`--conflict-resolve` controls how bisync handles such a scenario. The currently +supported options are: + +- `none` - (the default) - do not attempt to pick a winner, keep and rename +both files according to [`--conflict-loser`](#conflict-loser) and +[`--conflict-suffix`](#conflict-suffix) settings. For example, with the default +settings, `file.txt` on Path1 is renamed `file.txt.conflict1` and `file.txt` on +Path2 is renamed `file.txt.conflict2`. Both are copied to the opposite path +during the run, so both sides end up with a copy of both files. (As `none` is +the default, it is not necessary to specify `--conflict-resolve none` -- you +can just omit the flag.) +- `newer` - the newer file (by `modtime`) is considered the winner and is +copied without renaming. The older file (the "loser") is handled according to +`--conflict-loser` and `--conflict-suffix` settings (either renamed or +deleted.) For example, if `file.txt` on Path1 is newer than `file.txt` on +Path2, the result on both sides (with other default settings) will be `file.txt` +(winner from Path1) and `file.txt.conflict1` (loser from Path2). +- `older` - same as `newer`, except the older file is considered the winner, +and the newer file is considered the loser. +- `larger` - the larger file (by `size`) is considered the winner (regardless +of `modtime`, if any). +- `smaller` - the smaller file (by `size`) is considered the winner (regardless +of `modtime`, if any). +- `path1` - the version from Path1 is unconditionally considered the winner +(regardless of `modtime` and `size`, if any). This can be useful if one side is +usually more trusted or up-to-date than the other. +- `path2` - same as `path1`, except the path2 version is considered the +winner. + +For all of the above options, note the following: +- If either of the underlying remotes lacks support for the chosen method, it +will be ignored and fall back to `none`. (For example, if `--conflict-resolve +newer` is set, but one of the paths uses a remote that doesn't support +`modtime`.) +- If a winner can't be determined because the chosen method's attribute is +missing or equal, it will be ignored and fall back to `none`. (For example, if +`--conflict-resolve newer` is set, but the Path1 and Path2 modtimes are +identical, even if the sizes may differ.) +- If the file's content is currently identical on both sides, it is not +considered a "conflict", even if new or changed on both sides since the prior +sync. (For example, if you made a change on one side and then synced it to the +other side by other means.) Therefore, none of the conflict resolution flags +apply in this scenario. +- The conflict resolution flags do not apply during a `--resync`, as there is +no "prior run" to speak of (but see [`--resync-mode`](#resync-mode) for similar +options.) + +### --conflict-loser CHOICE {#conflict-loser} + +`--conflict-loser` determines what happens to the "loser" of a sync conflict +(when [`--conflict-resolve`](#conflict-resolve) determines a winner) or to both +files (when there is no winner.) The currently supported options are: + +- `num` - (the default) - auto-number the conflicts by automatically appending +the next available number to the `--conflict-suffix`, in chronological order. +For example, with the default settings, the first conflict for `file.txt` will +be renamed `file.txt.conflict1`. If `file.txt.conflict1` already exists, +`file.txt.conflict2` will be used instead (etc., up to a maximum of +9223372036854775807 conflicts.) +- `pathname` - rename the conflicts according to which side they came from, +which was the default behavior prior to `v1.66`. For example, with +`--conflict-suffix path`, `file.txt` from Path1 will be renamed +`file.txt.path1`, and `file.txt` from Path2 will be renamed `file.txt.path2`. +If two non-identical suffixes are provided (ex. `--conflict-suffix +cloud,local`), the trailing digit is omitted. Importantly, note that with +`pathname`, there is no auto-numbering beyond `2`, so if `file.txt.path2` +somehow already exists, it will be overwritten. Using a dynamic date variable +in your `--conflict-suffix` (see below) is one possible way to avoid this. Note +also that conflicts-of-conflicts are possible, if the original conflict is not +manually resolved -- for example, if for some reason you edited +`file.txt.path1` on both sides, and those edits were different, the result +would be `file.txt.path1.path1` and `file.txt.path1.path2` (in addition to +`file.txt.path2`.) +- `delete` - keep the winner only and delete the loser, instead of renaming it. +If a winner cannot be determined (see `--conflict-resolve` for details on how +this could happen), `delete` is ignored and the default `num` is used instead +(i.e. both versions are kept and renamed, and neither is deleted.) `delete` is +inherently the most destructive option, so use it only with care. + +For all of the above options, note that if a winner cannot be determined (see +`--conflict-resolve` for details on how this could happen), or if +`--conflict-resolve` is not in use, *both* files will be renamed. + +### --conflict-suffix STRING[,STRING] {#conflict-suffix} + +`--conflict-suffix` controls the suffix that is appended when bisync renames a +[`--conflict-loser`](#conflict-loser) (default: `conflict`). +`--conflict-suffix` will accept either one string or two comma-separated +strings to assign different suffixes to Path1 vs. Path2. This may be helpful +later in identifying the source of the conflict. (For example, +`--conflict-suffix dropboxconflict,laptopconflict`) + +With `--conflict-loser num`, a number is always appended to the suffix. With +`--conflict-loser pathname`, a number is appended only when one suffix is +specified (or when two identical suffixes are specified.) i.e. with +`--conflict-loser pathname`, all of the following would produce exactly the +same result: + +``` +--conflict-suffix path +--conflict-suffix path,path +--conflict-suffix path1,path2 +``` + +Suffixes may be as short as 1 character. By default, the suffix is appended +after any other extensions (ex. `file.jpg.conflict1`), however, this can be +changed with the [`--suffix-keep-extension`](/docs/#suffix-keep-extension) flag +(i.e. to instead result in `file.conflict1.jpg`). + +`--conflict-suffix` supports several *dynamic date variables* when enclosed in +curly braces as globs. This can be helpful to track the date and/or time that +each conflict was handled by bisync. For example: + +``` +--conflict-suffix {DateOnly}-conflict +// result: myfile.txt.2006-01-02-conflict1 +``` + +All of the formats described [here](https://pkg.go.dev/time#pkg-constants) and +[here](https://pkg.go.dev/time#example-Time.Format) are supported, but take +care to ensure that your chosen format does not use any characters that are +illegal on your remotes (for example, macOS does not allow colons in +filenames, and slashes are also best avoided as they are often interpreted as +directory separators.) To address this particular issue, an additional +`{MacFriendlyTime}` (or just `{mac}`) option is supported, which results in +`2006-01-02 0304PM`. + +Note that `--conflict-suffix` is entirely separate from rclone's main +[`--sufix`](/docs/#suffix-suffix) flag. This is intentional, as users may wish +to use both flags simultaneously, if also using +[`--backup-dir`](#backup-dir1-and-backup-dir2). + +Finally, note that the default in bisync prior to `v1.66` was to rename +conflicts with `..path1` and `..path2` (with two periods, and `path` instead of +`conflict`.) Bisync now defaults to a single dot instead of a double dot, but +additional dots can be added by including them in the specified suffix string. +For example, for behavior equivalent to the previous default, use: + +``` +[--conflict-resolve none] --conflict-loser pathname --conflict-suffix .path +``` + ### --check-sync Enabled by default, the check-sync function checks that all of the same @@ -610,7 +756,7 @@ the other side by moving the corresponding file from `gdrive:Bisync` to moves it from `/Users/someuser/some/local/path/Bisync` to `/Users/someuser/some/local/path/BackupDir`. -In the event of a `..path1` / `..path2` rename due to a sync conflict, the +In the event of a [rename due to a sync conflict](#conflict-loser), the rename is not considered a delete, unless a previous conflict with the same name already exists and would get overwritten. @@ -634,7 +780,8 @@ On each successive run it will: - Lock file prevents multiple simultaneous runs when taking a while. This can be particularly useful if bisync is run by cron scheduler. - Handle change conflicts non-destructively by creating - `..path1` and `..path2` file versions. + `.conflict1`, `.conflict2`, etc. file versions, according to + [`--conflict-resolve`](#conflict-resolve), [`--conflict-loser`](#conflict-loser), and [`--conflict-suffix`](#conflict-suffix) settings. - File system access health check using `RCLONE_TEST` files (see the `--check-access` flag). - Abort on excessive deletes - protects against a failed listing @@ -661,8 +808,8 @@ Path1 deleted | File no longer exists on Path1 | File is deleted Type | Description | Result | Implementation --------------------------------|---------------------------------------|------------------------------------|----------------------- Path1 new/changed AND Path2 new/changed AND Path1 == Path2 | File is new/changed on Path1 AND new/changed on Path2 AND Path1 version is currently identical to Path2 | No change | None -Path1 new AND Path2 new | File is new on Path1 AND new on Path2 (and Path1 version is NOT identical to Path2) | Files renamed to _Path1 and _Path2 | `rclone copy` _Path2 file to Path1, `rclone copy` _Path1 file to Path2 -Path2 newer AND Path1 changed | File is newer on Path2 AND also changed (newer/older/size) on Path1 (and Path1 version is NOT identical to Path2) | Files renamed to _Path1 and _Path2 | `rclone copy` _Path2 file to Path1, `rclone copy` _Path1 file to Path2 +Path1 new AND Path2 new | File is new on Path1 AND new on Path2 (and Path1 version is NOT identical to Path2) | Conflicts handled according to [`--conflict-resolve`](#conflict-resolve) & [`--conflict-loser`](#conflict-loser) settings | default: `rclone copy` renamed `Path2.conflict2` file to Path1, `rclone copy` renamed `Path1.conflict1` file to Path2 +Path2 newer AND Path1 changed | File is newer on Path2 AND also changed (newer/older/size) on Path1 (and Path1 version is NOT identical to Path2) | Conflicts handled according to [`--conflict-resolve`](#conflict-resolve) & [`--conflict-loser`](#conflict-loser) settings | default: `rclone copy` renamed `Path2.conflict2` file to Path1, `rclone copy` renamed `Path1.conflict1` file to Path2 Path2 newer AND Path1 deleted | File is newer on Path2 AND also deleted on Path1 | Path2 version survives | `rclone copy` Path2 to Path1 Path2 deleted AND Path1 changed | File is deleted on Path2 AND changed (newer/older/size) on Path1 | Path1 version survives |`rclone copy` Path1 to Path2 Path1 deleted AND Path2 changed | File is deleted on Path1 AND changed (newer/older/size) on Path2 | Path2 version survives | `rclone copy` Path2 to Path1 @@ -673,7 +820,7 @@ Now, when bisync comes to a file that it wants to rename (because it is new/chan it first checks whether the Path1 and Path2 versions are currently *identical* (using the same underlying function as [`check`](commands/rclone_check/).) If bisync concludes that the files are identical, it will skip them and move on. -Otherwise, it will create renamed `..Path1` and `..Path2` duplicates, as before. +Otherwise, it will create renamed duplicates, as before. This behavior also [improves the experience of renaming directories](https://forum.rclone.org/t/bisync-bugs-and-feature-requests/37636#:~:text=Renamed%20directories), as a `--resync` is no longer required, so long as the same change has been made on both sides. @@ -1604,6 +1751,7 @@ instead of of `--size-only`, when `check` is not available. * Bisync now supports a "Graceful Shutdown" mode to cleanly cancel a run early without requiring `--resync`. * New `--recover` flag allows robust recovery in the event of interruptions, without requiring `--resync`. * A new `--max-lock` setting allows lock files to automatically renew and expire, for better automatic recovery when a run is interrupted. +* Bisync now supports auto-resolving sync conflicts and customizing rename behavior with new [`--conflict-resolve`](#conflict-resolve), [`--conflict-loser`](#conflict-loser), and [`--conflict-suffix`](#conflict-suffix) flags. ### `v1.64` * Fixed an [issue](https://forum.rclone.org/t/bisync-bugs-and-feature-requests/37636#:~:text=1.%20Dry%20runs%20are%20not%20completely%20dry)