Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ public void willExcludePath(VirtualPath sourcePath) {
}

@Override
public void willIgnorePath(VirtualPath sourcePath) {
log.debug("Ignoring path {}", sourcePath);
public void willIgnoreSourcePath(VirtualPath sourcePath) {
log.debug("Ignoring source path {}", sourcePath);
}

@Override
public void willIgnoreTargetPath(VirtualPath targetPath) {
log.debug("Ignoring target path {}", targetPath);
}

@Override
Expand Down
77 changes: 25 additions & 52 deletions jsync-engine/src/main/java/com/fizzed/jsync/engine/JsyncEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fizzed.jsync.vfs.*;
import com.fizzed.jsync.vfs.util.Permissions;
import com.fizzed.jsync.vfs.util.VirtualPathMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -31,9 +32,10 @@ public class JsyncEngine {
private List<String> ignores;
// when running a sync
private Checksum negotiatedChecksum;
private List<VirtualPath> excludePaths;
private List<VirtualPath> ignoreSourcePaths;
private List<VirtualPath> ignoreTargetPaths;
private VirtualPathMatchers excludeMatchers;
private VirtualPathMatchers ignoreMatchers;
private VirtualPath sourceRootPath;
private VirtualPath targetRootPath;

public JsyncEngine() {
this.eventHandler = new DefaultJsyncEventHandler();
Expand Down Expand Up @@ -225,6 +227,9 @@ public JsyncResult sync(VirtualFileSystem sourceVfs, String sourcePath, VirtualF
final VirtualPath sourcePathAbsFinal = sourcePathAbs.normalize();
final VirtualPath targetPathAbsFinal = targetPathAbs.normalize();

this.sourceRootPath = sourcePathAbsFinal;
this.targetRootPath = targetPathAbsFinal;


//
// Negotiate checksum methods between source and target filesystems if necessary
Expand All @@ -236,29 +241,12 @@ public JsyncResult sync(VirtualFileSystem sourceVfs, String sourcePath, VirtualF
log.debug("Source filesystem stat mode: {}", sourceVfs.getStatModel());
log.debug("Target filesystem stat mode: {}", targetVfs.getStatModel());

// build exclude and ignore paths
if (this.excludes != null) {
this.excludePaths = this.excludes.stream()
.map(VirtualPath::parse)
.map(sourcePathAbsFinal::resolve)
.collect(toList());
} else {
this.excludePaths = Collections.emptyList();
}

if (this.ignores != null) {
this.ignoreSourcePaths = this.ignores.stream()
.map(VirtualPath::parse)
.map(sourcePathAbsFinal::resolve)
.collect(toList());
this.ignoreTargetPaths = this.ignores.stream()
.map(VirtualPath::parse)
.map(targetPathAbsFinal::resolve)
.collect(toList());
} else {
this.ignoreSourcePaths = Collections.emptyList();
this.ignoreTargetPaths = Collections.emptyList();
}
// build exclude and ignore matchers
this.excludeMatchers = VirtualPathMatchers.compile(this.excludes);
this.ignoreMatchers = VirtualPathMatchers.compile(this.ignores);

log.debug("Using exclude matchers: {}", this.excludeMatchers);
log.debug("Using ignore matchers: {}", this.ignoreMatchers);


final long now = System.currentTimeMillis();
Expand All @@ -272,17 +260,6 @@ public JsyncResult sync(VirtualFileSystem sourceVfs, String sourcePath, VirtualF
final List<VirtualPathPair> deferredFiles = new ArrayList<>();

if (sourcePathAbsFinal.isDirectory()) {
// any excludes, let's resolve them against pwd of the source to make it easier to exclude them
final List<VirtualPath> excludePaths;
if (this.excludes != null) {
excludePaths = this.excludes.stream()
.map(VirtualPath::parse)
.map(sourcePathAbsFinal::resolve)
.collect(toList());
} else {
excludePaths = Collections.emptyList();
}

// as we process files, only a subset may require more advanced methods of detecting whether they were modified
// since that process could be "expensive", we keep a list of files on source/target that we will defer processing
// until we have a chance to do some bulk processing of checksums, etc.
Expand Down Expand Up @@ -409,23 +386,20 @@ protected void syncDirectory(int level, JsyncResult result, List<VirtualPathPair


// we need a list of files in both directories, so we can see what to add/delete
List<VirtualPath> sourceChildPaths = sourceVfs.ls(sourcePath).stream()
final List<VirtualPath> sourceChildPaths = sourceVfs.ls(sourcePath).stream()
// apply filter to source files if they are on the exclude list
.filter(v -> {
for (VirtualPath p : this.excludePaths) {
if (v.startsWith(p)) {
this.eventHandler.willExcludePath(v);
return false;
}
if (this.excludeMatchers.matches(this.sourceRootPath, v)) {
this.eventHandler.willExcludePath(v);
return false;
}
return true;
})
.filter(v -> {
for (VirtualPath p : this.ignoreSourcePaths) {
if (v.startsWith(p)) {
this.eventHandler.willIgnorePath(v);
return false;
}
log.debug("Checking if should ignore: root={}, path={}", this.sourceRootPath, v);
if (this.ignoreMatchers.matches(this.sourceRootPath, v)) {
this.eventHandler.willIgnoreSourcePath(v);
return false;
}
return true;
})
Expand All @@ -446,10 +420,9 @@ protected void syncDirectory(int level, JsyncResult result, List<VirtualPathPair

final List<VirtualPath> targetChildPaths = targetVfs.ls(targetPath).stream()
.filter(v -> {
for (VirtualPath p : this.ignoreTargetPaths) {
if (v.startsWith(p)) {
return false;
}
if (this.ignoreMatchers.matches(this.targetRootPath, v)) {
this.eventHandler.willIgnoreTargetPath(v);
return false;
}
return true;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ public interface JsyncEventHandler {

void willEnd(VirtualFileSystem sourceVfs, VirtualPath sourcePath, VirtualFileSystem targetVfs, VirtualPath targetPath, JsyncResult result, long timeMillis);

void willExcludePath(VirtualPath targetPath);
void willExcludePath(VirtualPath sourcePath);

void willIgnorePath(VirtualPath targetPath);
void willIgnoreSourcePath(VirtualPath sourcePath);

void willIgnoreTargetPath(VirtualPath targetPath);

void willCreateDirectory(VirtualPath targetPath, boolean recursively);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.fizzed.jsync.vfs.util;

import com.fizzed.jsync.vfs.VirtualPath;

import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;

public class VirtualPathMatcher {

private final String globRule;
private final PathMatcher matcher;

public VirtualPathMatcher(String globRule, PathMatcher matcher) {
this.globRule = globRule;
this.matcher = matcher;
}

public boolean matches(VirtualPath rootPath, VirtualPath currentPath) {
final String relativePath;

// resolve the current path against the root path, so we're left with the relative path we're matching against
if (currentPath.isAbsolute()) {
String rootFullPath = rootPath.toFullPath();
String currentFullPath = currentPath.toFullPath();
int pathStartPos = currentFullPath.indexOf(rootFullPath);
if (pathStartPos >= 0) {
// remove the leading path PLUS the file separator
relativePath = currentFullPath.substring(pathStartPos + rootFullPath.length() + 1);
} else {
relativePath = currentFullPath;
}
} else {
relativePath = currentPath.toString();
}

return this.matcher.matches(Paths.get(relativePath));
}

@Override
public String toString() {
return this.globRule;
}

static public VirtualPathMatcher compile(String rule) {
String glob = rule.trim();
boolean isDirectory = false;
boolean isRooted = false;

// 1. Check for Directory marker
if (glob.endsWith("/")) {
isDirectory = true;
glob = glob.substring(0, glob.length() - 1); // Strip trailing slash
}

// 2. Check for Root anchor
if (glob.startsWith("/")) {
isRooted = true;
glob = glob.substring(1); // Strip leading slash
}

// FIX: Handle "/**/" usually found in the middle of paths
// Git: "docs/**/*.md" -> Zero or more dirs
// Java: "docs/**/*.md" -> One or more dirs (fails on docs/file.md)
// Solution: Replace "/**/" with "/{,**/}" which means "Empty OR /**/"
// if (glob.contains("/**/")) {
// glob = glob.replace("/**/", "/{,**/}");
// }

// 3. Build the Glob
// We need to construct a robust brace expansion {A,B,C...}
StringBuilder finalGlob = new StringBuilder();
finalGlob.append("glob:{");

if (isRooted) {
// Rule: /target/ or /target
finalGlob.append(glob); // Matches "target" at root
if (isDirectory) {
finalGlob.append(",").append(glob).append("/**"); // Matches "target/..." at root
}
} else {
// Rule: target/ or target
finalGlob.append(glob); // Matches "target" at root
finalGlob.append(",**/").append(glob); // Matches "src/target" (nested)

if (isDirectory) {
// If it's a directory, we must ALSO match the contents
finalGlob.append(",").append(glob).append("/**"); // Matches "target/file.txt" (root)
finalGlob.append(",**/").append(glob).append("/**"); // Matches "src/target/file.txt" (nested)
}
}

finalGlob.append("}");

String globRule = finalGlob.toString();
PathMatcher matcher = FileSystems.getDefault().getPathMatcher(globRule);

return new VirtualPathMatcher(globRule, matcher);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.fizzed.jsync.vfs.util;

import com.fizzed.jsync.vfs.VirtualPath;

import java.util.ArrayList;
import java.util.List;

public class VirtualPathMatchers {

private final List<VirtualPathMatcher> matchers;

public VirtualPathMatchers(List<VirtualPathMatcher> matchers) {
this.matchers = matchers;
}

public boolean matches(VirtualPath rootPath, VirtualPath path) {
for (VirtualPathMatcher matcher : this.matchers) {
if (matcher.matches(rootPath, path)) {
return true;
}
}
return false;
}

@Override
public String toString() {
return this.matchers.toString();
}

static public VirtualPathMatchers compile(List<String> rules) {
List<VirtualPathMatcher> matchers = new ArrayList<>();
if (rules != null) {
for (String rule : rules) {
matchers.add(VirtualPathMatcher.compile(rule));
}
}
return new VirtualPathMatchers(matchers);
}

}
Loading