Skip to content

Commit d0fe0e2

Browse files
authored
Merge pull request #39 from rofinn/rf/sync
Added a default sync function
2 parents b44425a + 47ed399 commit d0fe0e2

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

src/FilePathsBase.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export
3636
islink,
3737
cp,
3838
mv,
39-
remove,
39+
sync,
4040
tmpname,
4141
tmpdir,
4242
mktmp,

src/path.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,41 @@ function Base.mv(src::AbstractPath, dst::AbstractPath; force=false)
513513
rm(src; recursive=true)
514514
end
515515

516+
"""
517+
sync(src::AbstractPath, dst::AbstractPath; delete=false)
518+
519+
Recursively copy new and updated files from the source path to the
520+
destination. If delete is true then files at the destination that don't
521+
exist at the source will be removed.
522+
"""
523+
function sync(src::AbstractPath, dst::AbstractPath; delete=false)
524+
# Create an index of all of the source files
525+
index = Dict(Tuple(setdiff(p.segments, src.segments)) => p for p in walkpath(src))
526+
527+
if exists(dst)
528+
for p in walkpath(dst)
529+
k = Tuple(setdiff(p.segments, dst.segments))
530+
531+
if haskey(index, k)
532+
if modified(index[k]) > modified(p)
533+
cp(index[k], p; force=true)
534+
end
535+
536+
delete!(index, k)
537+
elseif delete
538+
rm(p; recursive=true)
539+
end
540+
end
541+
542+
# Finally, copy over files that don't exist at the destination
543+
for (seg, p) in index
544+
cp(p, Path(dst, tuple(dst.segments..., seg...)); force=true)
545+
end
546+
else
547+
cp(src, dst)
548+
end
549+
end
550+
516551
"""
517552
download(url::Union{AbstractString, AbstractPath}, localfile::AbstractPath)
518553

src/test.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ testsets = [
4343
test_mkdir,
4444
test_cp,
4545
test_mv,
46+
test_sync,
4647
test_symlink,
4748
test_touch,
4849
test_tmpname,
@@ -103,6 +104,7 @@ module TestPaths
103104
test_mkdir,
104105
test_cp,
105106
test_mv,
107+
test_sync,
106108
test_symlink,
107109
test_touch,
108110
test_tmpname,
@@ -608,6 +610,39 @@ module TestPaths
608610
end
609611
end
610612

613+
function test_sync(ps::PathSet)
614+
@testset "sync" begin
615+
# Base cp case
616+
sync(ps.foo, ps.qux / "foo")
617+
@test exists(ps.qux / "foo" / "baz.txt")
618+
619+
# Test that the copied baz file has a newer modified time
620+
baz_t = modified(ps.qux / "foo" / "baz.txt")
621+
@test modified(ps.baz) < baz_t
622+
623+
# Don't cp unchanged files when a new file is added
624+
# NOTE: sleep before we make a new file, so it's clear tha the
625+
# modified time has changed.
626+
sleep(1)
627+
write(ps.foo / "test.txt", "New File")
628+
sync(ps.foo, ps.qux / "foo")
629+
@test exists(ps.qux / "foo" / "test.txt")
630+
@test read(ps.qux / "foo" / "test.txt", String) == "New File"
631+
@test modified(ps.qux / "foo" / "baz.txt") == baz_t
632+
@test modified(ps.qux / "foo" / "test.txt") > baz_t
633+
634+
# Test not deleting a file on sync
635+
rm(ps.foo / "test.txt")
636+
sync(ps.foo, ps.qux / "foo")
637+
@test exists(ps.qux / "foo" / "test.txt")
638+
639+
# Test passing delete flag
640+
sync(ps.foo, ps.qux / "foo"; delete=true)
641+
@test !exists(ps.qux / "foo" / "test.txt")
642+
rm(ps.qux / "foo"; recursive=true)
643+
end
644+
end
645+
611646
function test_symlink(ps::PathSet)
612647
if ps.link
613648
@testset "symlink" begin

test/system.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ ps = PathSet(; symlink=true)
3232
test_mkdir,
3333
test_cp,
3434
test_mv,
35+
test_sync,
3536
test_symlink,
3637
test_touch,
3738
test_tmpname,

0 commit comments

Comments
 (0)