Smokes your problems, coughs fresh air.

Tag: diff

Replacing the full contents of a Subversion working (sub)dir

The annoyances that I suffered earlier today during the upgrade of a WordPress plugin made me turn to my favorite text-editor to create a simple script, svn-replace-dir:

usage() {
    cat <<"EOF"
$0 [--dry-run] <svn_dir> <replacement_dir>
This script replaces the contents of <svn_dir> with the contents of <replacement_dir>,
where <replacement_dir> is not an svn directory.
Copyleft 2010, Rowan Rodrik van der Molen <>
fatal_error() {
    -e "\e[1;31m$message\e[0m"
usage_error() {
    error="Wrong usage."
    [ -n "$1" ];
    -e "\e[1;31m$error\e[0m"
run_command() {
    -e "\e[1;34m$1\e[0m"
    [ $dry_run == 1 ] || $1
dry_run=0 [ $1 == '--dry-run' ];
[ $# == 2 ] || usage_error "Wrong number of arguments."
svn_dir= "$1"|sed -e 's#/$##'`
replacement_dir= "$2"|sed -e 's#/$##'`
#if [ "${svn_dir:0:1}" != "/" ]; then svn_dir="$PWD/$svn_dir"; fi
#if [ "${replacement_dir:0:1}" != "/" ]; then replacement_dir="$PWD/$replacement_dir"; fi
[ -d "$svn_dir" ] || usage_error "$svn_dir is not a directory."
[ -d "$replacement_dir" ] || usage_error "$replacement_dir is not a directory."
# Create all subdirectories in $svn_dir that do not yet exist
find . -mindepth 1  d -print | sed -e 's#^./##' | d;
    # Doesn't the destination directory already exist?
    [ ! -d "$d" ];
        run_command "svn mkdir '$d'"
# Copy all files from $replacement_dir to $svn_dir
find .  f -print | sed -e 's#^./##' | f;
    run_command "cp '$replacement_dir/$f' '$svn_dir/$f'" # FIXME: Quoting problem
# Remove all files that do no longer exist in $replacement dir
find .  f -print | grep -v '.svn' | f;
    [ ! -f "$begin_path/$replacement_dir/$f" ];
        run_command "svn rm '$f'"
# Remove all subdirs that do no longer exist in $replacement dir
find . -mindepth 1  d -print | grep -v '.svn' | d;
    [ ! -d "$begin_path/$replacement_dir/$d" ];
        run_command "svn rm '$d'"

Using the script is simple:

svn-replace-dir simple-tags new-simple-tags|less -R

It replaces all the contents of the first directory (simple-tags in the example) with those of the second directory and it deletes everything that is no longer present in the second dir. In the process, it does all the necessary calls to svn mkdir, svn rm and (in the next version) svn add.

diff tells me that the script has done its work correctly:

diff -x .svn -ruN simple-tags new-simple-tags
# Emptiness is bliss :-) 

This is another one of these occasions when Git would have made life so much easier. Luckily, at least there’s GitHub to host this script as a Gist. Check there if you want to fetch the newest version of this script.

Using diff and patch to upgrade web application installations

Update (July 30, 2008): I added information about making sure that the patch was successful.

When you install a big-ass web application such as WordPress or MediaWiki, you usually end with a bunch of configuration files and customizations (skins/themes, extension/plugins, uploads, etc.). This makes upgrading the files that come with the application a bit tricky. There’s a simple solution, however, which work regardless of whether you use a revision control system or not.

First of all, you do, of course, always need a revision control system. I personally recommend Git or Subversion, which are both excellent tools. But, that’s not what this post is about. I’m going to use two simple tools which are uniformly available on all (Unixy) platforms: diff and patch.

The procedure is simple:

  1. Download the version of the application which you’re currently running. For our example, we pretend that this version is extracted into the directory webapp-1.4.3.

  2. Then, download the version to which you’d like to upgrade. (We’re assuming that this version is extracted into the webapp-1.6.2 directory.)

  3. Compare the two versions to create a patch file:

    $ diff --unified --recursive --new-file webapp-1.4.3 webapp-1.6.2 > webapp-upgrade.diff
  4. Apply the patch to the installation of said web app:

    $ cd webapp-installed
    $ patch --strip=1 --remove-empty-files < ../webapp-upgrade.diff || echo "Some failures!"

Check if everything was patched perfectly

Now, if the patch command returned a non-zero status (printing Some failures! in the above example), it's time to check which chunks of which files failed. Get a summary by searching all files with an .rej or a .orig suffix:

$ find . -name "*.rej"

After manually applying any failed hunks, what's left is to compare your directory containing the patched application to the directory with the contents of the new application archive which you've used to create the patch:

$ cd ..
$ diff --unified --recursive --new-file webapp-1.6.2 webapp-installed

Version management

Your upgrade is done. Now, if your using a revision control system, you just need to check in new files and check out deleted files. In Subversion, I do this quickly using the following command sequence:

$ svn status|sed -e '/^\?/!d; s/^\?//'|xargs svn add
$ svn status|sed -e '/^\!/!d; s/^\!//'|xargs svn del

If you'd been using Git, you could do this all a little bit more sophisticatedly, but my Git skills are not advanced enough to go around giving others advice. Also, it's nice to learn a generic method before learning more specific tools.

© 2024 BigSmoke

Theme by Anders NorenUp ↑