Sync only changed files between two folders in Python
This code compares two folders and copies only the new or modified files from source to destination, skipping unchanged ones by comparing SHA-256 hashes.
Python code
36 linesimport hashlib
from pathlib import Path
import shutil
def file_hash(path: Path, chunk_size: int = 8192) -> str:
hasher = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(chunk_size), b""):
hasher.update(chunk)
return hasher.hexdigest()
def sync_files(src: str, dst: str) -> list[str]:
src_path = Path(src)
dst_path = Path(dst)
dst_path.mkdir(parents=True, exist_ok=True)
synced = []
for src_file in src_path.rglob("*"):
if src_file.is_file():
rel_path = src_file.relative_to(src_path)
dst_file = dst_path / rel_path
if not dst_file.exists() or file_hash(src_file) != file_hash(dst_file):
dst_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_file, dst_file)
synced.append(str(rel_path))
return synced
if __name__ == "__main__":
changed = sync_files("source_folder", "backup_folder")
if changed:
print("Synced files:")
for path in changed:
print(f" {path}")
else:
print("No changes detected — everything is up to date.")
Output
Synced files:
data.txt
reports/q1.csv
How it works
The script walks the entire source tree with Path.rglob("*"), so it captures files in subfolders too. For each file, it computes a SHA-256 hash in chunks, which avoids loading large files into memory all at once. It only copies when the destination file is missing or its hash differs, so unchanged files are skipped. Directories under the destination are created on demand with mkdir(parents=True, exist_ok=True). Using shutil.copy2 preserves metadata like timestamps, which is useful for backups.
Common mistakes
- Using `os.path.exists` on directories and forgetting that only files should be compared — always check `is_file()`.
- Hashing large files without chunking can exhaust memory; use a loop that reads fixed-size blocks.
- Forgetting to create destination subdirectories before copying, which raises `FileNotFoundError`.
- Comparing file modification times instead of content hashes may miss changes when timestamps are preserved but content differs.
Variations
- Use `filecmp.cmp(src_file, dst_file, shallow=False)` to compare content directly without computing hashes yourself.
- For very large trees, use `os.walk` combined with a set of relative paths to speed up the check.
Real-world use cases
- Incremental backups that copy only new or changed files to a backup location, saving time and storage.
- Deploying website assets or configs to a server by syncing only files that changed between the local and remote directory.
- Mirroring a dataset folder to a shared network drive, skipping unchanged files to minimize transfer time.
Sponsored
More from Files & data
- Append a Line to a Log File in Python easy
- Audit File Permissions Across a Project in Python easy
- Automatically Detect Corrupted Files Using SHA-256 Checksums in Python easy
- Automatically Highlight Data Validation Errors Inside Excel Files in Python easy
- Build a Command-Line To-Do List Application with Data Persistence in Python easy
- Build a File Index by Relative Path Hash Map in Python easy
Keep learning
Related tutorials and quizzes for this topic.