When synchronizing files between servers, developers often need more granular control than simple file copying. The ability to preview changes as diff patches before actual transfer helps in:
- Auditing security-sensitive modifications
- Verifying deployment changes in CI/CD pipelines
- Maintaining change documentation
While rsync doesn't natively output unified diffs, we can combine it with standard Unix tools:
rsync -avn --out-format="%f" source/ destination/ | \
while read file; do
diff -u "source/$file" "destination/$file" >> changes.patch
done
For a more robust solution handling edge cases:
#!/bin/bash
# Configurable parameters
SOURCE_DIR="/path/to/source"
DEST_DIR="/path/to/dest"
PATCH_FILE="$(date +%Y%m%d)-changes.patch"
# Generate differential patch
rsync -avn --delete --out-format="%n" "$SOURCE_DIR/" "$DEST_DIR/" | \
grep -v '/$' | \
while read -r file; do
if [ -f "$SOURCE_DIR/$file" ] && [ -f "$DEST_DIR/$file" ]; then
diff -u "$DEST_DIR/$file" "$SOURCE_DIR/$file" >> "$PATCH_FILE"
elif [ ! -f "$DEST_DIR/$file" ]; then
echo "+++ NEW FILE: $file" >> "$PATCH_FILE"
cat "$SOURCE_DIR/$file" | sed 's/^/+/' >> "$PATCH_FILE"
fi
done
echo "Patch generated: $PATCH_FILE"
This technique proves particularly valuable when:
- Config file management: Review exactly which lines differ in nginx/Apache configurations
- Code deployments: Verify changes between development and staging environments
- Compliance tracking: Maintain audit trails of file modifications
For large directory trees, consider these optimizations:
# Use checksum comparison for accuracy
rsync -acn --out-format="%f" source/ dest/
# Parallel processing for speed
find source/ -type f -print0 | parallel -0 -j8 diff -u {} dest/{/}
When synchronizing files between servers, many developers want to preview changes before executing the actual transfer. While rsync's -n
(dry run) flag shows which files will be modified, it doesn't reveal the content differences. Here's how to generate patch files that document these changes.
Rsync wasn't primarily designed as a diff tool, but we can combine it with other Unix utilities to achieve patch generation:
rsync -avn --out-format="%f" source/ destination/ | \
while read file; do
diff -u "source/$file" "destination/$file" >> changes.patch
done
For production use, consider this enhanced version that handles edge cases:
#!/bin/bash
SOURCE_DIR="/path/to/source/"
DEST_DIR="/path/to/dest/"
PATCH_FILE="rsync_changes_$(date +%Y%m%d).patch"
# First get the list of changed files
changed_files=$(rsync -aic --out-format="%f" "$SOURCE_DIR" "$DEST_DIR")
# Generate unified diff for each changed file
echo "" > "$PATCH_FILE" # Clear existing patch file
while IFS= read -r file; do
if [ -f "$SOURCE_DIR$file" ] && [ -f "$DEST_DIR$file" ]; then
diff -u "$SOURCE_DIR$file" "$DEST_DIR$file" >> "$PATCH_FILE"
fi
done <<< "$changed_files"
To apply the changes later:
patch -p0 < changes.patch
For binary files or more complex comparisons:
rsync -avc --dry-run --itemize-changes source/ dest/ | \
grep '^[><]' | awk '{print $2}' | \
xargs -I {} diff -u source/{} dest/{} > binary_changes.patch
- This method works best for text files
- Binary files require special handling
- Permission changes won't be captured in the patch
- New files will show as full diffs
For Git repositories, you can combine rsync with git diff:
rsync -avn --delete --exclude='.git' source/ dest/ | \
while read file; do
(cd source && git diff --no-index -- "$file" "../dest/$file")
done > git_changes.patch