Import patchsync version 2.4
[utils/utils.git] / patchsync
index 07e2f0f..ccd3390 100755 (executable)
--- a/patchsync
+++ b/patchsync
@@ -1,9 +1,11 @@
 #!/bin/bash
 # patchsync: Synchronizes a trunk, a branch, and a patch containing the
 # differences between them.
 #!/bin/bash
 # patchsync: Synchronizes a trunk, a branch, and a patch containing the
 # differences between them.
-# Version 2
 # -- Matt McCutchen
 # -- Matt McCutchen
-#
+
+# If I had to update the version in the --version message separately, I would forget.
+PATCHSYNC_VERSION=2.4
+
 # usage: patchsync [--dry-run] <staging> [branch | patch]
 #
 # Patchsync is invoked on a "staging directory", which holds some configuration
 # usage: patchsync [--dry-run] <staging> [branch | patch]
 #
 # Patchsync is invoked on a "staging directory", which holds some configuration
 #     - do_patch <patch> <convert-trunk-to-branch>: apply the patch to the
 #         specified trunk; define it to understand your favorite diff format
 #       - example: patch --no-backup-if-mismatch -d $2/ -p1 <$1
 #     - do_patch <patch> <convert-trunk-to-branch>: apply the patch to the
 #         specified trunk; define it to understand your favorite diff format
 #       - example: patch --no-backup-if-mismatch -d $2/ -p1 <$1
-#     - Note: patchsync runs these functions under "pipefail", but the
-#         "set -e" it uses does not propagate into the functions.  Patchsync
-#         provides an "exitoneok" function you can use to treat an exit code of
-#         1 as 0.  You might want to && successive commands together.
+#     - Note: Patchsync runs these functions under "pipefail" and "set -e".
+#         Caution: "set -e" is idiosyncratic; you may wish to && together
+#         successive commands anyway.  Patchsync provides an "exitoneok"
+#         function you can use to treat an exit code of 1 as 0.
 #     - There are several possible ways to handle failed hunks.  The simplest
 #         and safest is to make do_patch fail, but that's inconvenient for the
 #         user, who must investigate the *.rej files in the staging directory
 #     - There are several possible ways to handle failed hunks.  The simplest
 #         and safest is to make do_patch fail, but that's inconvenient for the
 #         user, who must investigate the *.rej files in the staging directory
 # Disable branch/.patchsync support because it's a bad idea in general, and the
 # cyclic symlink confuses Eclipse in particular. -- Matt 2006.11.30
 
 # Disable branch/.patchsync support because it's a bad idea in general, and the
 # cyclic symlink confuses Eclipse in particular. -- Matt 2006.11.30
 
-set -e
-trap "echo 'Patchsync encountered an unexpected error!  ABORTING!' 1>&2; exit 2;" ERR
+# Error handling
+function handle_error {
+       exec >&2
+       echo "Patchsync encountered an unexpected error!  Aborting!"
+       echo "The failed command was: $1"
+       exit 2
+}
+trap 'handle_error "$BASH_COMMAND"' ERR
 set -o errtrace
 set -o pipefail
 
 set -o errtrace
 set -o pipefail
 
@@ -90,7 +98,7 @@ set -o pipefail
 type rsync >/dev/null 2>&1 || \
        { echo "Patchsync requires rsync, but there's no rsync on your path!" 1>&2; exit 1; }
 # If a cp2 is available, use it; otherwise define our own.
 type rsync >/dev/null 2>&1 || \
        { echo "Patchsync requires rsync, but there's no rsync on your path!" 1>&2; exit 1; }
 # If a cp2 is available, use it; otherwise define our own.
-type cp2 >/dev/null 2>&1 || function cp2 { rsync -rltE --chmod=ugo=rwx "$@"; }
+type cp2 >/dev/null 2>&1 || function cp2 { exec rsync -rltE --chmod=ugo=rwx "$@"; }
 
 function exitoneok {
        "$@" || [ $? == 1 ]
 
 function exitoneok {
        "$@" || [ $? == 1 ]
@@ -106,7 +114,7 @@ function wdpp_from {
        # NOT bash builtin pwd; it tells us how we got here, not where we are
        pA="$(/bin/pwd)/"
        pA="${pA#/}"
        # NOT bash builtin pwd; it tells us how we got here, not where we are
        pA="$(/bin/pwd)/"
        pA="${pA#/}"
-       pB="$( (cd "$AtoB" && /bin/pwd) )/"
+       pB="$(cd "$AtoB" && /bin/pwd)/"
        pB="${pB#/}"
        # Lop off the longest common prefix of components that we can.
        # While first components are equal...
        pB="${pB#/}"
        # Lop off the longest common prefix of components that we can.
        # While first components are equal...
@@ -119,11 +127,12 @@ function wdpp_from {
        ans="$pA"
        # Translate remaining components of $pB to ../s
        while [ -n "$pB" ]; do
        ans="$pA"
        # Translate remaining components of $pB to ../s
        while [ -n "$pB" ]; do
-               ans="$ans../"
+               ans="../$ans"
                pB="${pB#*/}"
        done
        # Double check; add dot to the end to enforce ending in a slash and handle empty ans
        (cd "$AtoB" && [ "$ans." -ef /proc/self/fd/3 ]) 3<.
                pB="${pB#*/}"
        done
        # Double check; add dot to the end to enforce ending in a slash and handle empty ans
        (cd "$AtoB" && [ "$ans." -ef /proc/self/fd/3 ]) 3<.
+       [ $? == 0 ]
        # Yay
        echo "$ans"
 }
        # Yay
        echo "$ans"
 }
@@ -151,17 +160,11 @@ fi
 cd "$staging" || { echo "Failed to enter staging dir!" 1>&2; exit 1; }
 shift
 
 cd "$staging" || { echo "Failed to enter staging dir!" 1>&2; exit 1; }
 shift
 
-. settings
+. ./settings
 type do_diff >/dev/null 2>&1 || { echo "do_diff is not defined!" 1>&2; exit 1; }
 type do_patch >/dev/null 2>&1 || { echo "do_patch is not defined!" 1>&2; exit 1; }
 
 whichtoupdate="$1"
 type do_diff >/dev/null 2>&1 || { echo "do_diff is not defined!" 1>&2; exit 1; }
 type do_patch >/dev/null 2>&1 || { echo "do_patch is not defined!" 1>&2; exit 1; }
 
 whichtoupdate="$1"
-# patchsync --new doesn't need this any more except for identical-branch
-#if [ -z "$whichtoupdate" ] && [ -s whichtoupdate ]; then
-#      # Hook for patchsync --new
-#      whichtoupdate="$(< whichtoupdate)"
-#      echo "Updating $whichtoupdate according to staging dir."
-#el
 if [ -n "$whichtoupdate" ]; then
        echo "Updating $whichtoupdate according to command line argument."
 else
 if [ -n "$whichtoupdate" ]; then
        echo "Updating $whichtoupdate according to command line argument."
 else
@@ -211,6 +214,10 @@ if [ -z "$whichtoupdate" ] && [[ -n $trunkch || -n $branchch || -n $patchch ]];
        #echo "Synchronization will update $whichtoupdate."
 fi
 
        #echo "Synchronization will update $whichtoupdate."
 fi
 
+# Remove old copy-out files to be clean and to make sure we don't
+# mistakenly copy them out this time.
+rm -rf patch-new branch-new
+
 if [ -n "$whichtoupdate" ]; then
 
 # Always show what would happen if patch-new and branch-new were copied out.
 if [ -n "$whichtoupdate" ]; then
 
 # Always show what would happen if patch-new and branch-new were copied out.
@@ -219,15 +226,12 @@ if [ -n "$whichtoupdate" ]; then
 # state if no error.
 error=
 
 # state if no error.
 error=
 
-# Don't let stuff from an old run confuse us.
-rm -rf patch-new branch-new
-
 function prepare_branch {
        echo "Preparing updated branch..."
        # No link-dest because we will modify and then link-dest when copying out
        "${COPYIN[@]}" trunk/ branch-new/
 function prepare_branch {
        echo "Preparing updated branch..."
        # No link-dest because we will modify and then link-dest when copying out
        "${COPYIN[@]}" trunk/ branch-new/
-       do_patch patch branch-new || \
-               { error=1; echo "Failed to prepare updated branch!" 1>&2; rm -rf branch-new; }
+       (do_patch patch branch-new)
+       [ $? == 0 ] || { error=1; echo "Failed to prepare updated branch!" 1>&2; rm -rf branch-new; }
 }
 
 function prepare_patch {
 }
 
 function prepare_patch {
@@ -235,8 +239,8 @@ function prepare_patch {
        # Link-dest is fine because these are temporary read-only copies
        "${COPYIN[@]}" --link-dest=../trunk/ trunk/ trunk-tmp/
        "${COPYIN[@]}" --link-dest=../branch/ branch/ branch-tmp/
        # Link-dest is fine because these are temporary read-only copies
        "${COPYIN[@]}" --link-dest=../trunk/ trunk/ trunk-tmp/
        "${COPYIN[@]}" --link-dest=../branch/ branch/ branch-tmp/
-       do_diff trunk-tmp branch-tmp patch-new || \
-               { error=1; echo "Failed to prepare updated patch!" 1>&2; rm -rf patch-new; }
+       (do_diff trunk-tmp branch-tmp patch-new)
+       [ $? == 0 ] || { error=1; echo "Failed to prepare updated patch!" 1>&2; rm -rf patch-new; }
        rm -rf trunk-tmp branch-tmp
 }
 
        rm -rf trunk-tmp branch-tmp
 }
 
@@ -246,8 +250,8 @@ case $whichtoupdate in
        # No link-dest because we will link-dest when copying out
        "${COPYIN[@]}" trunk/ branch-new/
        echo "Creating empty patch..."
        # No link-dest because we will link-dest when copying out
        "${COPYIN[@]}" trunk/ branch-new/
        echo "Creating empty patch..."
-       do_diff branch-new branch-new patch-new || \
-               { error=1; echo "Failed to create empty patch!" 1>&2; rm -rf patch-new; }
+       (do_diff branch-new branch-new patch-new)
+       [ $? == 0 ] || { error=1; echo "Failed to create empty patch!" 1>&2; rm -rf patch-new; }
        ;;
 (branch)
        prepare_branch
        ;;
 (branch)
        prepare_branch
@@ -275,17 +279,14 @@ EOF
 esac
 
 if ! [ $error ] && ! [ $dryrun ]; then
 esac
 
 if ! [ $error ] && ! [ $dryrun ]; then
-       # Disable locking for now...
-       # ! [ -e lock ] || { echo "Staging dir is locked!  Delete the file \`lock' if the other instance of patchsync is gone." 1>&2; exit 1; }
-       # echo "patchsync lock file pid $$ date $(date)" >lock
-       
        echo "Copying out..."
        ! [ -e branch-new ] || {
                hash_dir branch-new/ >branch-new-hash
        echo "Copying out..."
        ! [ -e branch-new ] || {
                hash_dir branch-new/ >branch-new-hash
-               "${COPYOUT[@]}" -i --link-dest="$(wdpp_from branch/)branch-new/" branch-new/ branch/
+               linkdest="$(wdpp_from branch/)branch-new/" # Do separately so a failure in wdpp_from is noticed.
+               "${COPYOUT[@]}" -i --link-dest="$linkdest" branch-new/ branch/
                rm -rf branch-new
        }
                rm -rf branch-new
        }
-       ! [ -e patch-new ] || cmp -s patch-work patch || {
+       ! [ -e patch-new ] || cmp -s patch patch-new || {
                hash_file patch-new >patch-new-hash
                # Don't use rsync because we might have to write through a symlink.
                echo "> patch"
                hash_file patch-new >patch-new-hash
                # Don't use rsync because we might have to write through a symlink.
                echo "> patch"
@@ -297,16 +298,15 @@ if ! [ $error ] && ! [ $dryrun ]; then
        for i in trunk patch branch; do
                mv $i-new-hash $i-save-hash
        done
        for i in trunk patch branch; do
                mv $i-new-hash $i-save-hash
        done
-       
-       # rm lock
 else
        echo "Would copy out as follows:"
        ! [ -e branch-new ] || {
                hash_dir branch-new/ >branch-new-hash
 else
        echo "Would copy out as follows:"
        ! [ -e branch-new ] || {
                hash_dir branch-new/ >branch-new-hash
-               "${COPYOUT[@]}" -n -i --link-dest="$(wdpp_from branch/)branch-new/" branch-new/ branch/
+               linkdest="$(wdpp_from branch/)branch-new/" # Do separately so a failure in wdpp_from is noticed.
+               "${COPYOUT[@]}" -n -i --link-dest="$linkdest" branch-new/ branch/
                #rm -rf branch-new
        }
                #rm -rf branch-new
        }
-       ! [ -e patch-new ] || cmp -s patch-work patch || {
+       ! [ -e patch-new ] || cmp -s patch patch-new || {
                hash_file patch-new >patch-new-hash
                # Don't use rsync because we might have to write through a symlink.
                echo "> patch"
                hash_file patch-new >patch-new-hash
                # Don't use rsync because we might have to write through a symlink.
                echo "> patch"
@@ -347,13 +347,13 @@ function patchsync_new {
                exit 1
        fi
        
                exit 1
        fi
        
-       # Set up arguments.  Open templates because we will change directories.
+       # Set up arguments.
        trunk="$1"
        patch="$2"
        branch="$3"
        staging="$4"
        
        trunk="$1"
        patch="$2"
        branch="$3"
        staging="$4"
        
-       # What exists?  Whichtochange first?
+       # What exists?
        ! [ -e "$staging" ] || { echo "Staging dir already exists!" 1>&2; exit 1; }
        [ -d "$trunk" ] || { echo "Trunk does not exist!" 1>&2; exit 1; }
        
        ! [ -e "$staging" ] || { echo "Staging dir already exists!" 1>&2; exit 1; }
        [ -d "$trunk" ] || { echo "Trunk does not exist!" 1>&2; exit 1; }
        
@@ -364,9 +364,9 @@ function patchsync_new {
        echo "Created staging dir at $staging."
        
        # Adjust paths appropriately.
        echo "Created staging dir at $staging."
        
        # Adjust paths appropriately.
-       trunk="$wdpp$trunk"
-       patch="$wdpp$patch"
-       branch="$wdpp$branch"
+       [[ "$trunk" == /* ]] || trunk="$wdpp$trunk"
+       [[ "$patch" == /* ]] || patch="$wdpp$patch"
+       [[ "$branch" == /* ]] || branch="$wdpp$branch"
        
        # Create links to areas
        ln -s "$trunk" trunk
        
        # Create links to areas
        ln -s "$trunk" trunk
@@ -422,7 +422,7 @@ END
 
 function patchsync_help {
        cat <<EOF
 
 function patchsync_help {
        cat <<EOF
-Patchsync version 2 by Matt McCutchen
+Patchsync version $PATCHSYNC_VERSION by Matt McCutchen
 usage: patchsync [--dry-run] <staging> [branch | patch]
        patchsync --new <trunk> <patch> <branch> <staging>
 Please read the top of the script for complete documentation.
 usage: patchsync [--dry-run] <staging> [branch | patch]
        patchsync --new <trunk> <patch> <branch> <staging>
 Please read the top of the script for complete documentation.