You should be able to use a post-checkout hook to do almost what you need.
The limitation is that there is no pre-checkout hook, so you cannot abort.
Instead, you can only discard local changes to get a clean state.
If you want to, you can save the local changes with git stash save X
and creating an archive of any untracked files before they are removed.
You indicate that you would prefer a setting in global git config;
as previously suggested elsewhere on SO,
with git 1.7.1 or later, you can use the following:
git config --global init.templatedir '~/.git-template'
to create all repositories (via git init
or git clone
) with templated hooks.
The templated hooks are symbolic links that point into the repository itself,
so they won't have any effect when you create other (unrelated) repositories:
mkdir -p ~/.git-template/hooks && \
ln -sf ../../special-git-hooks/post-checkout ~/.git-template/hooks/.
The special-git-hooks/post-checkout
script is the one that will implement
the moral equivalent of git checkout --force
:
#!/bin/sh
#
# git hook to clean branch checkout (git checkout --force plus more)
#
# arguments to post-checkout (see `man githooks`)
OLD_REV=$1
NEW_REV=$2
BRANCH=$3
if [ 0 = "$BRANCH" -o x"$OLD_REV" = x"$NEW_REV"]; then
exit 0
fi
# Start from the repository root.
cd ./$(git rev-parse --show-cdup)
ARCHIVE=`mktemp -qu gitco-XXXXXXXXX`
git clean -nxd | sed 's/Would remove //' | cpio -oz --format ustar > $ARCHIVE.tgz
git add -f $ARCHIVE.tgz
git clean -fxd
git stash save $ARCHIVE
Note that this script doesn't handle files with newlines
or other non-printing characters in their names.
I'm working on a Perl version of this script that does.