Daily SVN Incremental Backup

Lately I’ve had a lot of repositories to deal with, so I wanted to back it up, beside doing the usual, creating a hotcopy I decided to create incremental backups of all the repositories, so we can restore them even if for some reason the hotcopy fails or the repository get’s corrupted, both of them the master and the hotcopy.

So I need to do this on windows, and on linux (ubuntu), but this should work on all the distributions of linux, provided you have the required programs installed, this is mostly for Cygwin, because usually the tools required come pre-installed on the distributions, and you also have to have installed Subversion and have it in your path.

#!/bin/bash

SCRIPT_NAME=`basename $0`

function help() {

    echo
    echo "Incremental dumping for SVN by Ilija Matoski (ilijamt@gmail.com)"
    echo "No command arguments detected, there need to be as following."
    echo "Usage: ${SCRIPT_NAME} <path_to_repo> <path_to_dump> <identifier>"
    echo
    echo "Parameters:"
    echo "  <path_to_repo> - Where is the repository located"
    echo "  <path_to_dump> - Where should we dump the repository"
    echo "  <identifier>   - Used to record the last recorded dump, always stored in the file with the"
    echo "                   dumps from the repo, so we can keep a track of where we at with dumping,"
    echo "                   also identifier is used to generate the dumps names."
    echo "  <compress>     - compress the output dump (default: false)"
    echo
    echo "Examples:"
    echo "  ${SCRIPT_NAME} test /backup/SVN/test test"
    echo "  ${SCRIPT_NAME} test /backup/SVN/test test true"
    echo

    exit -1

}

if [ ! $# -ge 3 ]; then
    help
fi

PATH_TO_REPO=$1
PATH_TO_DUMP=$2
IDENTIFIER=$3
COMPRESS=$4

# Check if they are empty and if they are quit
if [[ -z "$PATH_TO_REPO" || -z "$PATH_TO_DUMP" || -z "$IDENTIFIER" ]]; then
    help
fi

if [ "$COMPRESS" == "true" ]; then
    COMPRESS="true"
else
    COMPRESS="false"
fi

# Check if the repo path exists, and if it's a valid SVN repository
if [ ! -d "$PATH_TO_REPO" ]; then
    echo "Directory ${PATH_TO_REPO} doesn't exist."
    echo "Exiting."
    exit -1
fi

# Check if SVN folder is valid, check for format file in the directory
if [ ! -f "${PATH_TO_REPO}/format" ]; then
    echo "Probably not a valid SVN repository."
    echo "Exiting."
    exit -1
else
    echo "Probably a valid SVN repository detected. Continuing."
fi

# Check if destination exist and if we have writing permissions
# and if it doesn't then create it
if [ ! -d "$PATH_TO_DUMP" ]; then
    echo "Dump directory doesn't exist, creating a new directory: $PATH_TO_DUMP"
    mkdir -p "$PATH_TO_DUMP"
fi

# Check if the directory has writing permissions
if [ ! -w "$PATH_TO_DUMP" ]; then
    echo "No writing permissions."
    echo "Exiting."
    exit -1
fi

# Get the full paths if we have relative paths in the command line
PATH_TO_REPO=`cd $PATH_TO_REPO; pwd`
PATH_TO_DUMP=`cd $PATH_TO_DUMP; pwd`
IDENTIFIER_PATH="$PATH_TO_DUMP/$IDENTIFIER"
DUMP_FILE_MASK="$IDENTIFIER_PATH"
# Check if the identifier exists
if [ ! -f "$IDENTIFIER_PATH" ]; then
    # Doesn't exist, so we create it and set it to revision 0
    echo "-1" > "$IDENTIFIER_PATH"
fi

CYGWIN=`uname -o`

# Check if we are running cygwin, for windows
if [ "$CYGWIN" == "Cygwin" ]; then
    CYGWIN="true"
else
    CYGWIN="false"
fi

# Set up some stuff for cygwin
if [ "$CYGWIN" == "true" ]; then
    repoDrive="${PATH_TO_REPO:10:1}"
    REPO_URL=`echo $PATH_TO_REPO | sed "s/\/cygdrive\/${repoDrive}/${repoDrive}:/g"`
    REPO_DIR="$REPO_URL"
    REPO_URL="file:///${REPO_URL}"
else
    REPO_URL="file:///$PATH_TO_REPO"
    REPO_DIR="$PATH_TO_REPO"
fi

# Get the last revision from the directory repository
REPO_LAST_REVISION=`svn info $REPO_URL | grep "^Revision: " | sed "s/Revision: //g"`
REPO_DUMPED_REVISION=`cat "$PATH_TO_DUMP/$IDENTIFIER"`

# check if the call succeded
if [[ $REPO_LAST_REVISION  != [0-9]* ]]; then
    echo "Cannot fetch information from SVN repository"
    exit -1
fi

# Increment it by one number from the last revision
# so we don't re dump the last one again
REPO_DUMPED_REVISION=$(($REPO_DUMPED_REVISION+1))

echo "Settings"
echo "  Compress: ${COMPRESS}"
echo "  Directory: ${REPO_URL}"
echo "  Dump: ${PATH_TO_DUMP}"
echo "  Identifier: ${IDENTIFIER}"
echo "  Cygwin: ${CYGWIN}"
echo
echo "Repository"
echo "  Revisions: ${REPO_LAST_REVISION}"
echo "  Dumping from: ${REPO_DUMPED_REVISION}"
echo

if [ $REPO_DUMPED_REVISION -gt $REPO_LAST_REVISION ]; then
    echo "ERROR: Dumped revision bigger than the revision in the repository"
    echo "Exiting."
    exit -1
fi

echo "Starting to dump"

list=$(seq "$REPO_DUMPED_REVISION" 1 "$REPO_LAST_REVISION" )

for rev in $list
do

    padding_rev=`printf "%08d" $rev`
    dump_file="$DUMP_FILE_MASK.rev.$padding_rev.svndump"

    if [ "$COMPRESS" == "true" ]; then
        dump_file="${dump_file}.gz"
    fi

    echo "  Dumping revision $rev of $REPO_LAST_REVISION"

    if [ "$COMPRESS" == "true" ]; then
        svnadmin dump "$REPO_DIR" --revision "$rev:$rev" --incremental --deltas -q | gzip > $dump_file
    else
        svnadmin dump "$REPO_DIR" --revision "$rev:$rev" --incremental --deltas -q > $dump_file
    fi

    # put the new dumped revision in the file
    echo $rev > "$IDENTIFIER_PATH"

done

echo "Done."

It’s really easy to use this to create a incremental backup of your SVN repository. If you need help just write, without any arguments:

./incremental_backup.sh

And it will show you the help/usage screen.