-# patchsync: Synchronize a branch with a patch (e.g., acls.diff and rsync-acl).
-# usage: patchsync <staging> {'' | --dry-run} [branch | patch]
-# <staging>: the staging directory, containing a shell script "settings" and an optional rsync filter file "filter"
-# "settings" must contain:
-# - $trunk, $patch, $branch paths interpreted relative to the staging dir
-# - shell function do_diff: trunk, branch => patch
-# e.g., for rsync: diff -uprN trunk branch | sed -re 's/^(\+\+\+|---) ([^\t]+).*$/\1 \2/' | grep -v '^diff' >patch
-# - shell function do_patch: branch copied from trunk, patch => branch
-# e.g., for rsync: patch --no-backup-if-mismatch -d branch/ -p1 <patch
-# --dry-run: don't note anything
-# branch | patch: overwrite the specified thing in case of a conflict (e.g., on the first run)
+# patchsync: Synchronizes a trunk, a branch, and a patch containing the
+# differences between them.
+# Version 2
+# -- Matt McCutchen
+#
+# usage: patchsync [--dry-run] <staging> [branch | patch]
+#
+# Patchsync is invoked on a "staging directory", which holds some configuration
+# (including the locations of the trunk, patch, and branch it is to synchronize)
+# and some synchronization state. It determines whether each of the trunk,
+# patch, and branch has changed since the last successful synchronization and
+# updates the patch or branch as appropriate:
+#
+# Changed since last sync Patchsync's behavior
+# -------------------------------------------------
+# Nothing Do nothing
+# Trunk only Update branch
+# Patch but not branch Update branch
+# Branch but not patch Update patch
+# Branch and patch Complain about conflict
+#
+# <staging>: path to the staging directory
+#
+# --dry-run: show what would happen without actually modifying the trunk, patch,
+# branch, or synchronization state
+#
+# {branch | patch}: force patchsync to update the specified thing from the
+# others instead of deciding automatically; you can use this argument to
+# revert or to resolve a conflict
+#
+# CAVEAT: Patchsync might make a mess if the trunk, patch, or branch is
+# modified in a way not hidden by the filters while patchsync is running!
+#
+# CAVEAT: Patchsync only notices creations, deletions, and modifications of
+# regular files in the trunk and branch, not other changes like empty directory
+# creations. If you make a change like that to the trunk, you can force
+# patchsync to update the branch.
+#
+# Staging directory format: A staging directory contains the following items:
+# "trunk", trunk directory or symlink to it
+# "patch", patch regular file or symlink to it
+# "branch", branch directory or symlink to it
+# [Why symlinks? Expose as much as possible to tools like symlinks(8).]
+# "settings", shell script defining the following shell functions:
+# - do_diff <trunk> <branch> <write-patch>: diff the specified trunk and
+# branch and write the patch to the specified file; define it to use
+# your favorite diff format
+# - example: exitoneok diff -urN $1 $2 \
+# | sed -re 's/^(\+\+\+|---) ([^\t]+).*$/\1 \2/' \
+# | exitoneok grep -v '^diff' >$3
+# - 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.
+# - 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
+# and either fix the patch or fix the branch and force updating the
+# patch. One could make do_patch succeed, but if the user then modifies
+# the branch, the failed hunks will merely be dropped from the patch,
+# which is probably unacceptable. The clever way is to let do_patch
+# succeed but make do_diff fail if any *.rej files exist in the branch.
+# "filters" (optional): rsync filters to use when accessing the trunk and
+# branch; hide filters apply to reading, protect filters to writing;
+# hint: you probably want to hide and protect build outputs
+#
+# Other usage: patchsync --new <trunk> <patch> <branch> <staging>
+# Mostly sets up a new staging directory for the given trunk, branch, and patch
+# at the given location. You still have to provide settings, and filters if
+# you want them.
+# - If one of the patch or branch exists, the other will be calculated when
+# you first synchronize.
+# - If both exist, you will get a conflict when you first synchronize and you
+# will need to specify which to update.
+# - If neither exists, you get an empty patch and a branch identical to the trunk.
+
+# Disable branch/.patchsync support because it's a bad idea in general, and the
+# cyclic symlink confuses Eclipse in particular. -- Matt 2006.11.30