Breezer
Breezer

Reputation: 503

How to move fossil repository subdirectory tree (to elsewhere within same repository, retaining tree levels)

I have a directory with multiple levels of subdirectories inside a fossil checkout, that I want to move to another location in a different subdirectory and retain the multiple-level directory structure.

For instance, to move a1 into a2 below, to go from having (handwritten like an abbreviated find command output):

a1/
a1/b/
a1/b/files
a1/c/
a1/c/d/
a1/c/d/more-files
a2/

I want fossil mv --hard a1 a2 to result in:

a2/a1/
a2/a1/b/
a2/a1/b/files
a2/a1/c/
a2/a1/c/d/
a2/a1/c/d/more-files

Just like the normal unix mv command would result in. Ideally with the history of the mv kept so it can be merged into another branch with any changes to files and more-files intact; as I could just fossil remove the files then re-add them as fresh files, but this is an uglier solution than I'd like.

fossil mv command (in v1.33 on Linux) loses the multiple levels and I end up with all files from lower level subdirectories moved into the top level directory of the new location.

Upvotes: 2

Views: 554

Answers (2)

tonypdmtr
tonypdmtr

Reputation: 3225

I think there is a much simpler solution (this is under Windows but similarly for Linux)

md a2\a1
fossil mv a1 a2/a1 --hard

Upvotes: 1

Breezer
Breezer

Reputation: 503

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

Upvotes: 1

Related Questions