Fix syncback process to handle directory paths and class transitions in clean mode

- Resolved an issue where syncback failed for projects with `$path` pointing to single files by ensuring only directory paths are scanned for orphans.
- Implemented a function to create missing directories in clean mode, allowing restoration of deleted directories.
- Enhanced class transition handling in clean mode to allow recoverable transitions between Folder and Script types.
- Updated logging and error handling to improve clarity during the syncback process.
This commit is contained in:
Joe 2026-02-01 19:27:53 -06:00
parent fe1ac1dd95
commit 2e6f909a71
5 changed files with 194 additions and 27 deletions

View file

@ -230,17 +230,20 @@ pub fn syncback_loop_with_stats(
// not just instigating_source metadata (which may point to project file).
let mut dirs_to_scan: Vec<PathBuf> = Vec::new();
// Helper to recursively collect $path entries from project tree
// Helper to recursively collect $path DIRECTORY entries from project tree
// Only directories need to be scanned for orphan detection; single files
// don't contain orphans that need to be removed.
fn collect_paths_from_project(
node: &crate::project::ProjectNode,
base_path: &Path,
paths: &mut Vec<PathBuf>,
) {
// If this node has a $path, add it
// If this node has a $path that points to a directory, add it
if let Some(ref path_node) = node.path {
let resolved = base_path.join(path_node.path());
log::trace!("Found $path in project: {}", resolved.display());
if resolved.exists() && !paths.contains(&resolved) {
// Only add directories - single files don't need orphan scanning
if resolved.is_dir() && !paths.contains(&resolved) {
log::trace!("Found $path directory in project: {}", resolved.display());
paths.push(resolved);
}
}
@ -336,12 +339,8 @@ pub fn syncback_loop_with_stats(
for dir in &dirs_to_scan {
if dir.is_dir() {
scan_directory(dir, &mut paths, &ignore_patterns, project_path);
} else if dir.exists() {
// It's a file, add it directly
if is_valid_path(&ignore_patterns, project_path, dir) {
paths.insert(dir.clone());
}
}
// Note: We only add directories to dirs_to_scan now, so no else branch needed
}
log::debug!("Scanned {} existing paths from filesystem", paths.len());

View file

@ -30,6 +30,16 @@ pub struct SyncbackData<'sync> {
pub(super) stats: &'sync SyncbackStats,
}
impl<'sync> SyncbackData<'sync> {
/// Returns whether syncback is running in incremental mode.
/// In incremental mode, existing file structure is preserved.
/// In clean mode (non-incremental), all children are treated as new.
#[inline]
pub fn is_incremental(&self) -> bool {
self.incremental
}
}
pub struct SyncbackSnapshot<'sync> {
pub data: SyncbackData<'sync>,
pub old: Option<Ref>,