Building a Patch From Two Directories
The task of comparing two directories comes up fairly often, and the standard Unix diff+patch handles it reasonably well, but in my case I specifically needed to produce an archive of the changes. I could have written a C++/C# program in a few minutes, but I set myself the goal of using a proper Unix-native solution.
File listing
The first step was getting the file lists. Variations on the ls -R command give you the right files, but in an inconvenient form that needs further processing. By the time I got to ls -lR|tr -s ‘ ‘|cut -d ‘ ‘ -f 9, I decided I’d had enough, gave up on ls, and used find instead. The downside of that command is that it prepends a relative path to every file. You can avoid that by cd-ing into the folder first and running find ., but then for some unclear reason symlinks disappear from the list, and using the -type fl parameter doesn’t help. The proper fix turned out to be trimming the “extra” path with sed.
find $2 | sed -e "s:^$2/::" > files.txt
Iterating and comparing
I decided to iterate over every file in the second folder and check whether it exists in the first. A fairly simple loop calling cmp:
for f in $(cat files.txt)
do
if [ -f "$2/$f" ]
then
if ! cmp -s "$1/$f" "$2/$f"; then
echo $f >> toadd.txt
fi
fi
done
The [ -f “$2/$f” ] check is there to filter out directories. If the file doesn’t exist at path “$1/$f”, it also ends up in the list, which suited me fine.
Archive
The resulting toadd.txt list gets fed to the tar command. To get correct paths inside the archive, you need an extra cd:
cd $2 tar -czvf ../result.tar.gz --files-from ../toadd.txt cd ..
Epilogue
After an hour spent on this, I now have a utility that lets me compare, say, two firmware images for some device, or different versions of repositories, and get a tar.gz (or any other) archive of the differences as output. Of course, using diff and patch would give a much smaller result, but what you get this way is far more transparent and easier to use. One notable shortcoming of this approach – empty directories don’t make it into the archive.
Full script file: http://runserver.net/temp/ddiff.sh
Originally published 2010-10-19.
