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