Bash script to commit snapshots to SVN branch
Algorithm
-   For each snapshot 
- Update working copy
- Delete all non-directory files, leaving .svn meta-data intact
- Mark empty directories for SVN deletion
- Mark missing files for SVN deletion
- Mark new files for SVN addition
- Check that the working directory matches the snapshot exactly
- Commit changes
- Tag changes
 
Input
- LABELS: Path to text file containing ordered list of snapshots to process
- REPO_URL: SVN URL to project root
- WORKING_BRANCH: SVN branch to commit changes to
- TAG_BRANCH: SVN branch to create tags on
- ROOT: Local path to directory of snapshots
- WORKING: Local path to SVN working directory
Initial Conditions
- The WORKING directory contains a checkout of the WORKING_BRANCH (this would be trivial to add to the script)
Script
#!/bin/sh
REPO_URL="http://path/to/project"
WORKING_BRANCH="trunk"
TAG_BRANCH="tags"
ROOT="/path/to/snapshots" # root of snapshot dir
WORKING="$ROOT/svn/working/copy" # working copy
LABELS="$ROOT/snapshot_list"
VERBOSE=1
die() {
    echo ERROR: $1 ; exit 1
}
# labels pre-check
#
cat "$LABELS" | while read LABEL
do
    [ -d "$ROOT/$LABEL" ] || die "no label"
done
cat "$LABELS" | while read LABEL
do
    cd "$ROOT" || break
    [ -d "$ROOT/$LABEL" ] || die "no label"
    (($VERBOSE)) && echo $LABEL: found
    # update
    svn update $(cygpath -m "$WORKING") || die "svn update"
    (($VERBOSE)) && echo $LABEL: updated
    # clean working
    find "$WORKING" -name '.svn' -prune -o -type f -exec rm -f {} \;
    (($VERBOSE)) && echo $LABEL: cleaned
    # copy new snapshot
    cp -r "$ROOT/$LABEL"/* "$WORKING/"  || die "new snapshot"
    (($VERBOSE)) && echo $LABEL: new snapshot
    # Resolve
    #  Prune empty directories
    find "$WORKING" -name '.svn' -prune -o -type d -print | while read DIR
    do
        DIRCOUNT=$(find "$DIR" -mindepth 1 -maxdepth 1 -name '.svn' -prune -o -print | wc -l)
        if ! (($DIRCOUNT))
        then
            svn delete $(cygpath -m "$DIR")
        fi
    done
    (($VERBOSE)) && echo $LABEL: pruned empty
    # Delete missing
    svn status $(cygpath -m "$WORKING") | grep '\!' | awk '{print "\"" $2 "\"" ;}' | xargs svn rm
    (($VERBOSE)) && echo $LABEL: deleted missing
    # Add non-versioned
    svn status $(cygpath -m "$WORKING") | grep '\?' | awk '{print "\"" $2 "\"" ;}' | xargs svn add
    (($VERBOSE)) && echo $LABEL: added new
    # Sanity check
    diff -x .svn -r "$ROOT/$LABEL" "$WORKING/"  || (echo "diff failed: exit to continue" ; /bin/sh)
    # commit
    svn commit $(cygpath -m "$WORKING") -m "\"Import VSS_$LABEL\"" || die "commit failed"
    (($VERBOSE)) && echo $LABEL: committed
    #COMMIT_REV=$(svn log $(cygpath -m "$WORKING") --revision "HEAD" | head -2 | tail -1 | awk '{print $1}' | cut -c 2-)
    # tag
    svn copy -m "Tag VSS_$LABEL" "$REPO_URL/$WORKING_BRANCH" "$REPO_URL/$TAG_BRANCH/VSS_$LABEL" || die "tag failed"
    (($VERBOSE)) && echo $LABEL: tagged
done