bisync: full support for comparing checksum, size, modtime - fixes #5679 fixes #5683 fixes #5684 fixes #5675
Before this change, bisync could only detect changes based on modtime, and
would refuse to run if either path lacked modtime support. This made bisync
unavailable for many of rclone's backends. Additionally, bisync did not account
for the Fs's precision when comparing modtimes, meaning that they could only be
reliably compared within the same side -- not against the opposite side. Size
and checksum (even when available) were ignored completely for deltas.
After this change, bisync now fully supports comparing based on any combination
of size, modtime, and checksum, lifting the prior restriction on backends
without modtime support. The comparison logic considers the backend's
precision, hash types, and other features as appropriate.
The comparison features optionally use a new --compare flag (which takes any
combination of size,modtime,checksum) and even supports some combinations not
otherwise supported in `sync` (like comparing all three at the same time.) By
default (without the --compare flag), bisync inherits the same comparison
options as `sync` (that is: size and modtime by default, unless modified with
flags such as --checksum or --size-only.) If the --compare flag is set, it will
override these defaults.
If --compare includes checksum and both remotes support checksums but have no
hash types in common with each other, checksums will be considered only for
comparisons within the same side (to determine what has changed since the prior
sync), but not for comparisons against the opposite side. If one side supports
checksums and the other does not, checksums will only be considered on the side
that supports them. When comparing with checksum and/or size without modtime,
bisync cannot determine whether a file is newer or older -- only whether it is
changed or unchanged. (If it is changed on both sides, bisync still does the
standard equality-check to avoid declaring a sync conflict unless it absolutely
has to.)
Also included are some new flags to customize the checksum comparison behavior
on backends where hashes are slow or unavailable. --no-slow-hash and
--slow-hash-sync-only allow selectively ignoring checksums on backends such as
local where they are slow. --download-hash allows computing them by downloading
when (and only when) they're otherwise not available. Of course, this option
probably won't be practical with large files, but may be a good option for
syncing small-but-important files with maximum accuracy (for example, a source
code repo on a crypt remote.) An additional advantage over methods like
cryptcheck is that the original file is not required for comparison (for
example, --download-hash can be used to bisync two different crypt remotes with
different passwords.)
Additionally, all of the above are now considered during the final --check-sync
for much-improved accuracy (before this change, it only compared filenames!)
Many other details are explained in the included docs.
2023-12-01 08:44:38 +08:00
|
|
|
|
[36m(01) :[0m [34mtest changes compare-all[0m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[36m(02) :[0m [34mtest initial bisync[0m
|
|
|
|
|
[36m(03) :[0m [34mbisync resync compare-all[0m
|
|
|
|
|
INFO : Bisyncing with Comparison Settings:
|
|
|
|
|
{
|
|
|
|
|
"Modtime": true,
|
|
|
|
|
"Size": true,
|
|
|
|
|
"Checksum": true,
|
|
|
|
|
"NoSlowHash": false,
|
|
|
|
|
"SlowHashSyncOnly": false,
|
|
|
|
|
"DownloadHash": true
|
|
|
|
|
}
|
|
|
|
|
INFO : Synching Path1 "{path1/}" with Path2 "{path2/}"
|
|
|
|
|
INFO : Copying unique Path2 files to Path1
|
|
|
|
|
INFO : - [34mPath2[0m [35mResync is copying UNIQUE files to[0m - [36mPath1[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35mResync is copying UNIQUE OR DIFFERING files to[0m - [36mPath2[0m
|
|
|
|
|
INFO : Resync updating listings
|
|
|
|
|
INFO : [32mBisync successful[0m
|
|
|
|
|
|
|
|
|
|
[36m(04) :[0m [34mtest make modifications on both paths[0m
|
|
|
|
|
[36m(05) :[0m [34mtest new on path2 - file10[0m
|
|
|
|
|
[36m(06) :[0m [34mtouch-copy 2001-01-02 {datadir/}file10.txt {path2/}[0m
|
|
|
|
|
|
|
|
|
|
[36m(07) :[0m [34mtest newer on path2 - file1[0m
|
|
|
|
|
[36m(08) :[0m [34mtouch-copy 2001-01-02 {datadir/}file1.txt {path2/}[0m
|
|
|
|
|
|
|
|
|
|
[36m(09) :[0m [34mtest new on path1 - file11[0m
|
|
|
|
|
[36m(10) :[0m [34mtouch-copy 2001-01-02 {datadir/}file11.txt {path1/}[0m
|
|
|
|
|
|
|
|
|
|
[36m(11) :[0m [34mtest newer on path1 - file2[0m
|
|
|
|
|
[36m(12) :[0m [34mtouch-copy 2001-01-02 {datadir/}file2.txt {path1/}[0m
|
|
|
|
|
|
|
|
|
|
[36m(13) :[0m [34mtest deleted on path2 - file3[0m
|
|
|
|
|
[36m(14) :[0m [34mdelete-file {path2/}file3.txt[0m
|
|
|
|
|
|
|
|
|
|
[36m(15) :[0m [34mtest deleted on path1 - file4[0m
|
|
|
|
|
[36m(16) :[0m [34mdelete-file {path1/}file4.txt[0m
|
|
|
|
|
|
|
|
|
|
[36m(17) :[0m [34mtest deleted on both paths - file8[0m
|
|
|
|
|
[36m(18) :[0m [34mdelete-file {path1/}file8.txt[0m
|
|
|
|
|
[36m(19) :[0m [34mdelete-file {path2/}file8.txt[0m
|
|
|
|
|
|
|
|
|
|
[36m(20) :[0m [34mtest changed on both paths - file5 (file5R, file5L)[0m
|
|
|
|
|
[36m(21) :[0m [34mtouch-glob 2001-01-02 {datadir/} file5R.txt[0m
|
|
|
|
|
[36m(22) :[0m [34mcopy-as {datadir/}file5R.txt {path2/} file5.txt[0m
|
|
|
|
|
[36m(23) :[0m [34mtouch-glob 2001-03-04 {datadir/} file5L.txt[0m
|
|
|
|
|
[36m(24) :[0m [34mcopy-as {datadir/}file5L.txt {path1/} file5.txt[0m
|
|
|
|
|
|
|
|
|
|
[36m(25) :[0m [34mtest newer on path2 and deleted on path1 - file6[0m
|
|
|
|
|
[36m(26) :[0m [34mtouch-copy 2001-01-02 {datadir/}file6.txt {path2/}[0m
|
|
|
|
|
[36m(27) :[0m [34mdelete-file {path1/}file6.txt[0m
|
|
|
|
|
|
|
|
|
|
[36m(28) :[0m [34mtest newer on path1 and deleted on path2 - file7[0m
|
|
|
|
|
[36m(29) :[0m [34mtouch-copy 2001-01-02 {datadir/}file7.txt {path1/}[0m
|
|
|
|
|
[36m(30) :[0m [34mdelete-file {path2/}file7.txt[0m
|
|
|
|
|
|
|
|
|
|
[36m(31) :[0m [34mtest bisync run[0m
|
|
|
|
|
[36m(32) :[0m [34mbisync compare-all[0m
|
|
|
|
|
INFO : Bisyncing with Comparison Settings:
|
|
|
|
|
{
|
|
|
|
|
"Modtime": true,
|
|
|
|
|
"Size": true,
|
|
|
|
|
"Checksum": true,
|
|
|
|
|
"NoSlowHash": false,
|
|
|
|
|
"SlowHashSyncOnly": false,
|
|
|
|
|
"DownloadHash": true
|
|
|
|
|
}
|
|
|
|
|
INFO : Synching Path1 "{path1/}" with Path2 "{path2/}"
|
|
|
|
|
INFO : Building Path1 and Path2 listings
|
|
|
|
|
INFO : Path1 checking for diffs
|
|
|
|
|
INFO : - [36mPath1[0m [35m[33mFile changed: [35msize (larger)[0m, [35mtime (newer)[0m, [35mhash[0m[0m[0m - [36mfile2.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[31mFile was deleted[0m[0m - [36mfile4.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[33mFile changed: [35msize (larger)[0m, [35mtime (newer)[0m, [35mhash[0m[0m[0m - [36mfile5.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[31mFile was deleted[0m[0m - [36mfile6.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[33mFile changed: [35msize (larger)[0m, [35mtime (newer)[0m, [35mhash[0m[0m[0m - [36mfile7.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[31mFile was deleted[0m[0m - [36mfile8.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[32mFile is new[0m[0m - [36mfile11.txt[0m
|
|
|
|
|
INFO : Path1: 7 changes: [32m 1 new[0m, [33m 3 modified[0m, [31m 3 deleted[0m
|
|
|
|
|
INFO : ([33mModified[0m: [36m 3 newer[0m, [34m 0 older[0m, [36m 3 larger[0m, [34m 0 smaller[0m, [36m 3 hash differs[0m)
|
|
|
|
|
INFO : Path2 checking for diffs
|
|
|
|
|
INFO : - [34mPath2[0m [35m[33mFile changed: [35msize (larger)[0m, [35mtime (newer)[0m, [35mhash[0m[0m[0m - [36mfile1.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[31mFile was deleted[0m[0m - [36mfile3.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[33mFile changed: [35msize (larger)[0m, [35mtime (newer)[0m, [35mhash[0m[0m[0m - [36mfile5.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[33mFile changed: [35msize (larger)[0m, [35mtime (newer)[0m, [35mhash[0m[0m[0m - [36mfile6.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[31mFile was deleted[0m[0m - [36mfile7.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[31mFile was deleted[0m[0m - [36mfile8.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[32mFile is new[0m[0m - [36mfile10.txt[0m
|
|
|
|
|
INFO : Path2: 7 changes: [32m 1 new[0m, [33m 3 modified[0m, [31m 3 deleted[0m
|
|
|
|
|
INFO : ([33mModified[0m: [36m 3 newer[0m, [34m 0 older[0m, [36m 3 larger[0m, [34m 0 smaller[0m, [36m 3 hash differs[0m)
|
|
|
|
|
INFO : Applying changes
|
|
|
|
|
INFO : - [36mPath1[0m [35m[32mQueue copy to[0m Path2[0m - [36m{path2/}file11.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[32mQueue copy to[0m Path2[0m - [36m{path2/}file2.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[31mQueue delete[0m[0m - [36m{path2/}file4.txt[0m
|
|
|
|
|
NOTICE: - [34mWARNING[0m [35mNew or changed in both paths[0m - [36mfile5.txt[0m
|
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
```
2023-12-15 20:47:15 +08:00
|
|
|
|
NOTICE: - [36mPath1[0m [35mRenaming Path1 copy[0m - [36m{path1/}file5.txt.conflict1[0m
|
|
|
|
|
NOTICE: - [36mPath1[0m [35m[32mQueue copy to[0m Path2[0m - [36m{path2/}file5.txt.conflict1[0m
|
|
|
|
|
NOTICE: - [34mPath2[0m [35mRenaming Path2 copy[0m - [36m{path2/}file5.txt.conflict2[0m
|
|
|
|
|
NOTICE: - [34mPath2[0m [35m[32mQueue copy to[0m Path1[0m - [36m{path1/}file5.txt.conflict2[0m
|
bisync: full support for comparing checksum, size, modtime - fixes #5679 fixes #5683 fixes #5684 fixes #5675
Before this change, bisync could only detect changes based on modtime, and
would refuse to run if either path lacked modtime support. This made bisync
unavailable for many of rclone's backends. Additionally, bisync did not account
for the Fs's precision when comparing modtimes, meaning that they could only be
reliably compared within the same side -- not against the opposite side. Size
and checksum (even when available) were ignored completely for deltas.
After this change, bisync now fully supports comparing based on any combination
of size, modtime, and checksum, lifting the prior restriction on backends
without modtime support. The comparison logic considers the backend's
precision, hash types, and other features as appropriate.
The comparison features optionally use a new --compare flag (which takes any
combination of size,modtime,checksum) and even supports some combinations not
otherwise supported in `sync` (like comparing all three at the same time.) By
default (without the --compare flag), bisync inherits the same comparison
options as `sync` (that is: size and modtime by default, unless modified with
flags such as --checksum or --size-only.) If the --compare flag is set, it will
override these defaults.
If --compare includes checksum and both remotes support checksums but have no
hash types in common with each other, checksums will be considered only for
comparisons within the same side (to determine what has changed since the prior
sync), but not for comparisons against the opposite side. If one side supports
checksums and the other does not, checksums will only be considered on the side
that supports them. When comparing with checksum and/or size without modtime,
bisync cannot determine whether a file is newer or older -- only whether it is
changed or unchanged. (If it is changed on both sides, bisync still does the
standard equality-check to avoid declaring a sync conflict unless it absolutely
has to.)
Also included are some new flags to customize the checksum comparison behavior
on backends where hashes are slow or unavailable. --no-slow-hash and
--slow-hash-sync-only allow selectively ignoring checksums on backends such as
local where they are slow. --download-hash allows computing them by downloading
when (and only when) they're otherwise not available. Of course, this option
probably won't be practical with large files, but may be a good option for
syncing small-but-important files with maximum accuracy (for example, a source
code repo on a crypt remote.) An additional advantage over methods like
cryptcheck is that the original file is not required for comparison (for
example, --download-hash can be used to bisync two different crypt remotes with
different passwords.)
Additionally, all of the above are now considered during the final --check-sync
for much-improved accuracy (before this change, it only compared filenames!)
Many other details are explained in the included docs.
2023-12-01 08:44:38 +08:00
|
|
|
|
INFO : - [34mPath2[0m [35m[32mQueue copy to[0m Path1[0m - [36m{path1/}file6.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[32mQueue copy to[0m Path2[0m - [36m{path2/}file7.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[32mQueue copy to[0m Path1[0m - [36m{path1/}file1.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35m[32mQueue copy to[0m Path1[0m - [36m{path1/}file10.txt[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35m[31mQueue delete[0m[0m - [36m{path1/}file3.txt[0m
|
|
|
|
|
INFO : - [34mPath2[0m [35mDo queued copies to[0m - [36mPath1[0m
|
|
|
|
|
INFO : - [36mPath1[0m [35mDo queued copies to[0m - [36mPath2[0m
|
|
|
|
|
INFO : Updating listings
|
|
|
|
|
INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}"
|
|
|
|
|
INFO : [32mBisync successful[0m
|