Import patchsync version 1
[utils/utils.git] / patchsync
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