| 1 | #!/bin/bash |
| 2 | # patchsync: Synchronize a branch with a patch (e.g., acls.diff and rsync-acl). |
| 3 | # usage: patchsync <staging> {'' | --dry-run} [branch | patch] |
| 4 | # <staging>: the staging directory, containing a shell script "settings" and an optional rsync filter file "filter" |
| 5 | # "settings" must contain: |
| 6 | # - $trunk, $patch, $branch paths interpreted relative to the staging dir |
| 7 | # - shell function do_diff: trunk, branch => patch |
| 8 | # e.g., for rsync: diff -uprN trunk branch | sed -re 's/^(\+\+\+|---) ([^\t]+).*$/\1 \2/' | grep -v '^diff' >patch |
| 9 | # - shell function do_patch: branch copied from trunk, patch => branch |
| 10 | # e.g., for rsync: patch --no-backup-if-mismatch -d branch/ -p1 <patch |
| 11 | # --dry-run: don't note anything |
| 12 | # branch | patch: overwrite the specified thing in case of a conflict (e.g., on the first run) |
| 13 | |
| 14 | set -e |
| 15 | |
| 16 | staging="$1" |
| 17 | if ! [ -r "$staging/settings" ]; then |
| 18 | echo "Specify a staging directory containing a settings file!" 1>&2 |
| 19 | exit 1 |
| 20 | fi |
| 21 | cd "$staging" |
| 22 | |
| 23 | dryrun="$2" |
| 24 | whichtochange="$3" |
| 25 | CP2t=(cp2 --del) |
| 26 | CP2in=("${CP2t[@]}" --filter='. filter' --delete-excluded) |
| 27 | CP2out=("${CP2t[@]}" --filter='. filter' --no-t --checksum) # be nice to mtimes |
| 28 | export LC_COLLATE="C" |
| 29 | |
| 30 | . settings |
| 31 | |
| 32 | echo "Copying in, checking for changes..." |
| 33 | "${CP2in[@]}" --link-dest=../trunk-save -i "$trunk/" trunk/ |
| 34 | diff -r trunk{-save,} >/dev/null || trunkch=ch |
| 35 | "${CP2in[@]}" --link-dest=../branch-save -i "$branch/" branch/ |
| 36 | diff -r branch{-save,} >/dev/null || branchch=ch |
| 37 | cp2 --link-dest=../patch-save -i "$patch" patch |
| 38 | diff -r patch{-save,} >/dev/null || patchch=ch |
| 39 | echo "Done." |
| 40 | |
| 41 | if [ $trunkch ] || [ $branchch ] || [ $patchch ]; then |
| 42 | # Something changed. |
| 43 | # Update either branch or patch, whichever didn't change. |
| 44 | # If trunk changes, update branch, not patch. |
| 45 | if [ "$whichtochange" == 'branch' ] || ! [ $branchch ]; then |
| 46 | echo "Updating branch..." |
| 47 | "${CP2t[@]}" trunk/ branch/ |
| 48 | do_patch |
| 49 | echo "Done." |
| 50 | copyout=1 |
| 51 | elif [ "$whichtochange" == 'patch' ] || ! [ $patchch ]; then |
| 52 | echo "Updating patch..." |
| 53 | do_diff |
| 54 | echo "Done." |
| 55 | copyout=1 |
| 56 | else |
| 57 | echo "Conflict: both branch and patch changed!" |
| 58 | echo "Run patchsync <staging> <dry-run> [branch | patch] to" |
| 59 | echo "update the specified area from the others." |
| 60 | exit 1 |
| 61 | fi |
| 62 | else |
| 63 | # Easy case |
| 64 | echo "Nothing changed." |
| 65 | fi |
| 66 | |
| 67 | if [ $copyout ]; then |
| 68 | if ! [ $dryrun ]; then |
| 69 | ! [ -e lock ] || { echo "Locked! Please fix!"; exit 1; } |
| 70 | echo "patchsync lock file pid $$ date $(date)" >lock |
| 71 | |
| 72 | echo "Copying out..." |
| 73 | "${CP2out[@]}" -i branch/ "$branch/" |
| 74 | cp2 -i --checksum patch "$patch" |
| 75 | echo "Done." |
| 76 | echo "Noting..." |
| 77 | for i in trunk branch patch; do |
| 78 | rm -rf $i-save |
| 79 | mv $i{,-save} |
| 80 | done |
| 81 | echo "Done." |
| 82 | |
| 83 | rm lock |
| 84 | else |
| 85 | echo "Dry run; no action. You can inspect the results if you want." |
| 86 | echo "Fake copying out..." |
| 87 | "${CP2out[@]}" -in branch/ "$branch/" |
| 88 | cp2 -in --checksum patch "$patch" |
| 89 | echo "Done." |
| 90 | fi |
| 91 | fi |
| 92 | |
| 93 | exit 0 |