From 3b49fd24d4e9eebe04e676c680a49056714c5ace Mon Sep 17 00:00:00 2001 From: nielash Date: Sun, 9 Feb 2025 21:13:55 -0500 Subject: [PATCH] bisync: fix listings missing concurrent modifications - fixes #8359 Before this change, there was a bug affecting listing files when: - a given bisync run had changes in the 2to1 direction AND - the run had NO changes in the 1to2 direction AND - at least one of the changed files changed AGAIN during the run (specifically, after the initial march and before the transfers.) In this situation, the listings on one side would still retain the prior version of the changed file, potentially causing conflicts or errors. This change fixes the issue by making sure that if we're updating the listings on one side, we must also update the other. (We previously tried to skip it for efficiency, but this failed to account for the possibility that a changed file could change again during the run.) --- cmd/bisync/bisync_test.go | 10 +++ cmd/bisync/deltas.go | 16 ++-- cmd/bisync/operations.go | 18 +---- ...testdir_path1.._testdir_path2.copy2to1.que | 1 + .../_testdir_path1.._testdir_path2.path1.lst | 10 +++ ...estdir_path1.._testdir_path2.path1.lst-new | 10 +++ ...estdir_path1.._testdir_path2.path1.lst-old | 10 +++ .../_testdir_path1.._testdir_path2.path2.lst | 10 +++ ...estdir_path1.._testdir_path2.path2.lst-new | 10 +++ ...estdir_path1.._testdir_path2.path2.lst-old | 10 +++ .../testdata/test_concurrent/golden/test.log | 73 +++++++++++++++++++ .../test_concurrent/initial/RCLONE_TEST | 1 + .../test_concurrent/initial/file1.txt | 0 .../test_concurrent/initial/file2.txt | 0 .../test_concurrent/initial/file3.txt | 0 .../test_concurrent/initial/file4.txt | 0 .../test_concurrent/initial/file5.txt | 0 .../test_concurrent/initial/file6.txt | 0 .../test_concurrent/initial/file7.txt | 0 .../test_concurrent/initial/file8.txt | 0 .../test_concurrent/modfiles/dummy.txt | 0 .../test_concurrent/modfiles/file1.txt | 1 + .../test_concurrent/modfiles/file10.txt | 1 + .../test_concurrent/modfiles/file11.txt | 1 + .../test_concurrent/modfiles/file2.txt | 1 + .../test_concurrent/modfiles/file5L.txt | 1 + .../test_concurrent/modfiles/file5R.txt | 1 + .../test_concurrent/modfiles/file6.txt | 1 + .../test_concurrent/modfiles/file7.txt | 1 + .../testdata/test_concurrent/scenario.txt | 15 ++++ docs/content/bisync.md | 3 + 31 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.copy2to1.que create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-new create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-old create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-new create mode 100644 cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-old create mode 100644 cmd/bisync/testdata/test_concurrent/golden/test.log create mode 100644 cmd/bisync/testdata/test_concurrent/initial/RCLONE_TEST create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file1.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file2.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file3.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file4.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file5.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file6.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file7.txt create mode 100644 cmd/bisync/testdata/test_concurrent/initial/file8.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/dummy.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file1.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file10.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file11.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file2.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file5L.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file5R.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file6.txt create mode 100644 cmd/bisync/testdata/test_concurrent/modfiles/file7.txt create mode 100644 cmd/bisync/testdata/test_concurrent/scenario.txt diff --git a/cmd/bisync/bisync_test.go b/cmd/bisync/bisync_test.go index da89c3e20..f154d4d28 100644 --- a/cmd/bisync/bisync_test.go +++ b/cmd/bisync/bisync_test.go @@ -746,6 +746,16 @@ func (b *bisyncTest) runTestStep(ctx context.Context, line string) (err error) { case "test-func": b.TestFn = testFunc return + case "concurrent-func": + b.TestFn = func() { + src := filepath.Join(b.dataDir, "file7.txt") + dst := "file1.txt" + err := b.copyFile(ctx, src, b.replaceHex(b.path2), dst) + if err != nil { + fs.Errorf(src, "error copying file: %v", err) + } + } + return case "fix-names": // in case the local os converted any filenames ci.NoUnicodeNormalization = true diff --git a/cmd/bisync/deltas.go b/cmd/bisync/deltas.go index 1cb783d56..dde42e414 100644 --- a/cmd/bisync/deltas.go +++ b/cmd/bisync/deltas.go @@ -286,7 +286,7 @@ func (b *bisyncRun) findDeltas(fctx context.Context, f fs.Fs, oldListing string, } // applyDeltas -func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (changes1, changes2 bool, results2to1, results1to2 []Results, queues queues, err error) { +func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (results2to1, results1to2 []Results, queues queues, err error) { path1 := bilib.FsPath(b.fs1) path2 := bilib.FsPath(b.fs2) @@ -367,7 +367,7 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change } } - //if there are potential conflicts to check, check them all here (outside the loop) in one fell swoop + // if there are potential conflicts to check, check them all here (outside the loop) in one fell swoop matches, err := b.checkconflicts(ctxCheck, filterCheck, b.fs1, b.fs2) for _, file := range ds1.sort() { @@ -392,7 +392,7 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change } else if d2.is(deltaOther) { b.indent("!WARNING", file, "New or changed in both paths") - //if files are identical, leave them alone instead of renaming + // if files are identical, leave them alone instead of renaming if (dirs1.has(file) || dirs1.has(alias)) && (dirs2.has(file) || dirs2.has(alias)) { fs.Infof(nil, "This is a directory, not a file. Skipping equality check and will not rename: %s", file) ls1.getPut(file, skippedDirs1) @@ -486,7 +486,6 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change // Do the batch operation if copy2to1.NotEmpty() && !b.InGracefulShutdown { - changes1 = true b.indent("Path2", "Path1", "Do queued copies to") ctx = b.setBackupDir(ctx, 1) results2to1, err = b.fastCopy(ctx, b.fs2, b.fs1, copy2to1, "copy2to1") @@ -498,12 +497,11 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change return } - //copy empty dirs from path2 to path1 (if --create-empty-src-dirs) + // copy empty dirs from path2 to path1 (if --create-empty-src-dirs) b.syncEmptyDirs(ctx, b.fs1, copy2to1, dirs2, &results2to1, "make") } if copy1to2.NotEmpty() && !b.InGracefulShutdown { - changes2 = true b.indent("Path1", "Path2", "Do queued copies to") ctx = b.setBackupDir(ctx, 2) results1to2, err = b.fastCopy(ctx, b.fs1, b.fs2, copy1to2, "copy1to2") @@ -515,7 +513,7 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change return } - //copy empty dirs from path1 to path2 (if --create-empty-src-dirs) + // copy empty dirs from path1 to path2 (if --create-empty-src-dirs) b.syncEmptyDirs(ctx, b.fs2, copy1to2, dirs1, &results1to2, "make") } @@ -523,7 +521,7 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change if err = b.saveQueue(delete1, "delete1"); err != nil { return } - //propagate deletions of empty dirs from path2 to path1 (if --create-empty-src-dirs) + // propagate deletions of empty dirs from path2 to path1 (if --create-empty-src-dirs) b.syncEmptyDirs(ctx, b.fs1, delete1, dirs1, &results2to1, "remove") } @@ -531,7 +529,7 @@ func (b *bisyncRun) applyDeltas(ctx context.Context, ds1, ds2 *deltaSet) (change if err = b.saveQueue(delete2, "delete2"); err != nil { return } - //propagate deletions of empty dirs from path1 to path2 (if --create-empty-src-dirs) + // propagate deletions of empty dirs from path1 to path2 (if --create-empty-src-dirs) b.syncEmptyDirs(ctx, b.fs2, delete2, dirs2, &results1to2, "remove") } diff --git a/cmd/bisync/operations.go b/cmd/bisync/operations.go index 6a1d9d1fa..75b36e8b0 100644 --- a/cmd/bisync/operations.go +++ b/cmd/bisync/operations.go @@ -359,8 +359,6 @@ func (b *bisyncRun) runLocked(octx context.Context) (err error) { // Determine and apply changes to Path1 and Path2 noChanges := ds1.empty() && ds2.empty() - changes1 := false // 2to1 - changes2 := false // 1to2 results2to1 := []Results{} results1to2 := []Results{} @@ -370,7 +368,7 @@ func (b *bisyncRun) runLocked(octx context.Context) (err error) { fs.Infof(nil, "No changes found") } else { fs.Infof(nil, "Applying changes") - changes1, changes2, results2to1, results1to2, queues, err = b.applyDeltas(octx, ds1, ds2) + results2to1, results1to2, queues, err = b.applyDeltas(octx, ds1, ds2) if err != nil { if b.InGracefulShutdown && (err == context.Canceled || err == accounting.ErrorMaxTransferLimitReachedGraceful || strings.Contains(err.Error(), "context canceled")) { fs.Infof(nil, "Ignoring sync error due to Graceful Shutdown: %v", err) @@ -395,21 +393,11 @@ func (b *bisyncRun) runLocked(octx context.Context) (err error) { } b.saveOldListings() // save new listings - // NOTE: "changes" in this case does not mean this run vs. last run, it means start of this run vs. end of this run. - // i.e. whether we can use the March lst-new as this side's lst without modifying it. if noChanges { b.replaceCurrentListings() } else { - if changes1 || b.InGracefulShutdown { // 2to1 - err1 = b.modifyListing(fctx, b.fs2, b.fs1, results2to1, queues, false) - } else { - err1 = bilib.CopyFileIfExists(b.newListing1, b.listing1) - } - if changes2 || b.InGracefulShutdown { // 1to2 - err2 = b.modifyListing(fctx, b.fs1, b.fs2, results1to2, queues, true) - } else { - err2 = bilib.CopyFileIfExists(b.newListing2, b.listing2) - } + err1 = b.modifyListing(fctx, b.fs2, b.fs1, results2to1, queues, false) // 2to1 + err2 = b.modifyListing(fctx, b.fs1, b.fs2, results1to2, queues, true) // 1to2 } if b.DebugName != "" { l1, _ := b.loadListing(b.listing1) diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.copy2to1.que b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.copy2to1.que new file mode 100644 index 000000000..029f3e190 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.copy2to1.que @@ -0,0 +1 @@ +"file1.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst new file mode 100644 index 000000000..021dde2dc --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/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" +- 19 - - 2023-08-26T00:00:00.000000000+0000 "file1.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file3.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file4.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file5.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file6.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file7.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file8.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-new b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-new new file mode 100644 index 000000000..021dde2dc --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-new @@ -0,0 +1,10 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 19 - - 2023-08-26T00:00:00.000000000+0000 "file1.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file3.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file4.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file5.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file6.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file7.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file8.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-old b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path1.lst-old new file mode 100644 index 000000000..021dde2dc --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/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" +- 19 - - 2023-08-26T00:00:00.000000000+0000 "file1.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file3.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file4.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file5.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file6.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file7.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file8.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst new file mode 100644 index 000000000..021dde2dc --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/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" +- 19 - - 2023-08-26T00:00:00.000000000+0000 "file1.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file3.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file4.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file5.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file6.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file7.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file8.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-new b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-new new file mode 100644 index 000000000..021dde2dc --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-new @@ -0,0 +1,10 @@ +# bisync listing v1 from test +- 109 - - 2000-01-01T00:00:00.000000000+0000 "RCLONE_TEST" +- 19 - - 2023-08-26T00:00:00.000000000+0000 "file1.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file3.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file4.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file5.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file6.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file7.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file8.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-old b/cmd/bisync/testdata/test_concurrent/golden/_testdir_path1.._testdir_path2.path2.lst-old new file mode 100644 index 000000000..021dde2dc --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/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" +- 19 - - 2023-08-26T00:00:00.000000000+0000 "file1.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file2.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file3.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file4.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file5.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file6.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file7.txt" +- 0 - - 2000-01-01T00:00:00.000000000+0000 "file8.txt" diff --git a/cmd/bisync/testdata/test_concurrent/golden/test.log b/cmd/bisync/testdata/test_concurrent/golden/test.log new file mode 100644 index 000000000..35f894aed --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/golden/test.log @@ -0,0 +1,73 @@ +(01) : test concurrent + +(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 Path2 files to Path1 +INFO : - Path2 Resync is copying files to - Path1 +INFO : - Path1 Resync is copying files to - Path2 +INFO : Resync updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(04) : test changed on one path - file1 +(05) : touch-glob 2001-01-02 {datadir/} file5R.txt +(06) : touch-glob 2023-08-26 {datadir/} file7.txt +(07) : copy-as {datadir/}file5R.txt {path2/} file1.txt + +(08) : test bisync with file changed during +(09) : concurrent-func +(10) : bisync +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 : 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 : - Path2 Queue copy to Path1 - {path1/}file1.txt +INFO : - Path2 Do queued copies to - Path1 +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful + +(11) : bisync +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 : Path2 checking for diffs +INFO : No changes found +INFO : Updating listings +INFO : Validating listings for Path1 "{path1/}" vs Path2 "{path2/}" +INFO : Bisync successful diff --git a/cmd/bisync/testdata/test_concurrent/initial/RCLONE_TEST b/cmd/bisync/testdata/test_concurrent/initial/RCLONE_TEST new file mode 100644 index 000000000..d8ca97c2a --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/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_concurrent/initial/file1.txt b/cmd/bisync/testdata/test_concurrent/initial/file1.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file2.txt b/cmd/bisync/testdata/test_concurrent/initial/file2.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file3.txt b/cmd/bisync/testdata/test_concurrent/initial/file3.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file4.txt b/cmd/bisync/testdata/test_concurrent/initial/file4.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file5.txt b/cmd/bisync/testdata/test_concurrent/initial/file5.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file6.txt b/cmd/bisync/testdata/test_concurrent/initial/file6.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file7.txt b/cmd/bisync/testdata/test_concurrent/initial/file7.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/initial/file8.txt b/cmd/bisync/testdata/test_concurrent/initial/file8.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/dummy.txt b/cmd/bisync/testdata/test_concurrent/modfiles/dummy.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file1.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file1.txt new file mode 100644 index 000000000..464147f09 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file1.txt @@ -0,0 +1 @@ +This file is newer diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file10.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file10.txt new file mode 100644 index 000000000..464147f09 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file10.txt @@ -0,0 +1 @@ +This file is newer diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file11.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file11.txt new file mode 100644 index 000000000..464147f09 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file11.txt @@ -0,0 +1 @@ +This file is newer diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file2.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file2.txt new file mode 100644 index 000000000..0fd70321a --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file2.txt @@ -0,0 +1 @@ +Newer version \ No newline at end of file diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file5L.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file5L.txt new file mode 100644 index 000000000..43ceff1db --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file5L.txt @@ -0,0 +1 @@ +This file is newer and not equal to 5R diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file5R.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file5R.txt new file mode 100644 index 000000000..a928fcf13 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file5R.txt @@ -0,0 +1 @@ +This file is newer and not equal to 5L diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file6.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file6.txt new file mode 100644 index 000000000..464147f09 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file6.txt @@ -0,0 +1 @@ +This file is newer diff --git a/cmd/bisync/testdata/test_concurrent/modfiles/file7.txt b/cmd/bisync/testdata/test_concurrent/modfiles/file7.txt new file mode 100644 index 000000000..464147f09 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/modfiles/file7.txt @@ -0,0 +1 @@ +This file is newer diff --git a/cmd/bisync/testdata/test_concurrent/scenario.txt b/cmd/bisync/testdata/test_concurrent/scenario.txt new file mode 100644 index 000000000..19ca9fcc2 --- /dev/null +++ b/cmd/bisync/testdata/test_concurrent/scenario.txt @@ -0,0 +1,15 @@ +test concurrent + +test initial bisync +bisync resync + +test changed on one path - file1 +touch-glob 2001-01-02 {datadir/} file5R.txt +touch-glob 2023-08-26 {datadir/} file7.txt +copy-as {datadir/}file5R.txt {path2/} file1.txt + +test bisync with file changed during +concurrent-func +bisync + +bisync \ No newline at end of file diff --git a/docs/content/bisync.md b/docs/content/bisync.md index 458f92eab..3aa0bae54 100644 --- a/docs/content/bisync.md +++ b/docs/content/bisync.md @@ -1815,6 +1815,9 @@ about _Unison_ and synchronization in general. ## Changelog +### `v1.69.1` +* Fixed an issue causing listings to not capture concurrent modifications under certain conditions + ### `v1.68` * Fixed an issue affecting backends that round modtimes to a lower precision.