diff --git a/sync-config b/sync-config index f3b8664..dbfe7d7 100755 --- a/sync-config +++ b/sync-config @@ -68,6 +68,26 @@ copy_file() { fi } +delete_file() { + local dst="$1" + if $dry_run; then + echo "delete: $dst" + else + rm -f "$dst" + echo "deleted: $dst" + fi +} + +delete_dir() { + local dst="$1" + if $dry_run; then + echo "delete dir: $dst" + else + rm -rf "$dst" + echo "deleted dir: $dst" + fi +} + if [[ ! -d "$src_dir" ]]; then echo "Error: source config directory not found: $src_dir" >&2 exit 1 @@ -104,11 +124,55 @@ if [[ ! -e "$base_path" ]]; then exit 1 fi +check_delete_marker() { + local rel_path="$1" + + # Check centralized .delete folder: config/.delete/path/to/dir_or_file + if [[ -f "$src_dir/.delete/$rel_path" ]] || [[ -d "$src_dir/.delete/$rel_path" ]]; then + return 0 + fi + + # Check inline .delete marker: config/path/to/.delete/dir_or_file + local parent_path="$src_dir/$rel_path" + local parent_dir=$(dirname "$parent_path") + local base_name=$(basename "$parent_path") + if [[ -f "$parent_dir/.delete/$base_name" ]] || [[ -d "$parent_dir/.delete/$base_name" ]]; then + return 0 + fi + + return 1 +} + +# Check if a path is a .delete directory or inside one (should be skipped during traversal) +is_in_delete_dir() { + local rel_path="$1" + # Check if the path itself is a .delete directory or any parent is .delete + local current="$rel_path" + while [[ "$current" != "." && "$current" != "/" && -n "$current" ]]; do + local base + base=$(basename "$current") + if [[ "$base" == ".delete" ]]; then + return 0 + fi + current=$(dirname "$current") + done + return 1 +} + sync_file() { local src_path="$1" local rel_path=${src_path#"$src_dir"/} local dst_path="$dest_dir/$rel_path" + # Check if there's a corresponding .delete marker for this file + if check_delete_marker "$rel_path"; then + if [[ -f "$dst_path" ]]; then + delete_file "$dst_path" + deleted_files+=("$dst_path") + fi + return + fi + if [[ ! -f "$dst_path" ]]; then copy_file "$src_path" "$dst_path" return @@ -135,9 +199,55 @@ elif [[ -d "$base_path" ]]; then continue fi + # Check if this is a .delete directory - process its contents to delete in destination + if [[ -d "$src_path" && "$(basename "$rel_path")" == ".delete" ]]; then + # Get the parent directory of the .delete folder + parent_rel_path=$(dirname "$rel_path") + # Process all files in this .delete directory to delete corresponding files in destination + for delete_marker in "$src_path"/**; do + if [[ -e "$delete_marker" ]]; then + # Get the basename of the marker (file/dir to delete) + marker_basename=$(basename "$delete_marker") + # Construct the target path in the parent directory + target_rel_path="$parent_rel_path/$marker_basename" + target_dst_path="$dest_dir/$target_rel_path" + + if [[ -d "$delete_marker" ]]; then + if [[ -d "$target_dst_path" ]]; then + delete_dir "$target_dst_path" + fi + elif [[ -f "$delete_marker" ]]; then + if [[ -f "$target_dst_path" ]]; then + delete_file "$target_dst_path" + fi + fi + fi + done + # Delete the .delete folder in destination if it exists + delete_dst_path="$dest_dir/$rel_path" + if [[ -d "$delete_dst_path" ]]; then + delete_dir "$delete_dst_path" + fi + continue + fi + + # Skip anything inside a .delete directory - don't process or copy + if is_in_delete_dir "$rel_path"; then + continue + fi + dst_path="$dest_dir/$rel_path" if [[ -d "$src_path" ]]; then + # Check if there's a .delete marker for this directory + if check_delete_marker "$rel_path"; then + if [[ -d "$dst_path" ]]; then + delete_dir "$dst_path" + deleted_dirs+=("$dst_path") + fi + continue + fi + if ! $dry_run; then mkdir -p "$dst_path" else