One solution was to write a script to move each directoy individually, a level at a time, so it retained the structure. I would like to suggest this functionality to the fossil developer(s). I may post the script (below, with another dependency script included below that) to my github (user jgbreezer) sometime, but for now, this script (which I called fossilmvtree). It ignores files in the checkout not in fossil and will leave the old files/dirs where there are any (I don't believe it deletes them):
#!/bin/bash
# $1=source tree
# $2=dest. dir
# supports fossil mv options
# moves single source tree as-is to under/new dest.dir (not reducing dir levels to flat structure under dest dir)
exclude=''
usage () {
cat >&2 <<EOF
Usage: fossilmvtree [-x|--exclude= exclude_dirname] source dest"
-x option may be specified multiple times; do not specify full paths, just last
(filename/aka basename) of a directory to exclude from the move.
Command-line arguments are always included.
EOF
}
while [ -z "${1##-*}" ]
do
case "$1" in
-x|--exclude|--exclude=*)
if [[ "${1#--exclude=}" == "$1" ]]
then
# separate arg, '--exclude=' not used
shift
arg="$1"
else
arg="${1#--exclude=}"
fi
excinfo="$excinfo $arg"
# pruning is efficient
exclude="$exclude -type d -name '${arg//\'/\\\'}' -prune -o"
;;
--case-sensitive)
fossilopts="$fossilopts $1 $2"; shift;;
-*)
fossilopts="$fossilopts $1";;
esac
shift
done
echo "excluding paths: $excinfo"
echo "fossil mv options: $fossilopts"
[ $# -eq 2 ] || { usage; exit 1; }
mv="$(which fossilmvrev 2>/dev/null)" || { usage; echo "error:Missing fossilmvrev" >&2; exit 1; }
src="$1"
srcdir="$(basename "$src")"
dst="$2"
if [ -f "$dst" ]
then
# move src to new subdir of dst; otherwise we're renaming and moving
[ -d "$dst" ] || { echo "error:Destination '$dst' exists but is not a directory" >&2; exit 1; }
dst="$dst/$srcdir"
fi
#could set safe PATH (-execdir is cautious of relative/empty paths in $PATH but fossil binary might not be in std.location): PATH=/bin:/usr/bin:/usr/local/bin
eval find "$src" $exclude -type d -printf '%P\\n' | {
while read -r dir
do
[ -z "$dir" ] || [[ "$src/$dir" == "$dst/$dir" ]] && continue
echo
echo "fossil mv $src/$dir/* $dst/$dir/"
mkdir -p "$dst/$dir" || exit 1
find "$src/$dir" -maxdepth 1 \! -type d -exec "$mv" $fossilopts "$dst/$dir" '{}' +
rmdir "$src/$dir" # tidy up, as we only moved the files above (fossil doesn't really manage dirs)
# if rmdir fails due to remaining files, let user manage that (rmdir will complain to stderr if so) -
# likely to be unversioned files they might have forgotten about, shouldn't delete without user's knowledge.
done
}
It was only really tested once or twice on my specific fossil checkout, though written ready to be a re-usable script; please check the diffs (suggest do a clean checkout somewhere else and run it on that, then diff against your regular one using "diff -qr" or something before committing to check it behaved itself).
Careful if using the -x/exclude option, I wasn't sure that worked properly.
It depends on fossilmvrev script:
#!/bin/sh
# switch order of move arguments around to work with find -exec ... +
opts=''
while [ -z "${1##-*}" ]
do
case "$1" in
--case-sensitive) opts="$opts $1 $2"; shift 2;;
*) opts="$opts $1"; shift;;
esac
done
destdir="$1"
shift
fossil mv $opts "$@" $destdir