diff --git a/meta/git/hooks/pre-commit b/meta/git/hooks/pre-commit index 2032ada..c35c910 100755 --- a/meta/git/hooks/pre-commit +++ b/meta/git/hooks/pre-commit @@ -63,3 +63,118 @@ git diff --staged --name-only --diff-filter=AT $against \ done [ "$abort" -eq 0 ] || die } || exit + +# Make sure that a deletion does not break any symlinks (including renaming a +# file) +# TODO: switch all these to null-terminated lines +deleted_files="$(git diff-index --cached --name-only --diff-filter=D $against)" +if [ -n "$deleted_files" ]; then + # First, check for broken symlinks in the tree + all_broken_links="$(find . -xtype l -exec stat -c '%N' '{}' '+')" + + # NOTE: The cat could be replaced by instead adding the heredoc to the + # `done` of the loop, but would make the code much less readable + cat < *}" + source="${source#[\"\']}" + + if [ -z "${target##/*}" ]; then + # absolute link + if [ "$target" = "$PWD/$deletion" ]; then + die "You broke the symlink $link" + fi + else + # relative link + target="$(realpath -m "$source/../$target")" + if [ "$target" = "$PWD/$deletion" ]; then + die "You broke the symlink $link" + fi + fi + done || exit + done || exit + + # Second, check all symlinks in the index if they still point to the + # deleted file + all_links_in_index="$( + git ls-files --format="%(objectmode) %(objectname) %(path)" \ + | grep '^120000' + )" + + cat < \"$target\"" + fi + else + # relative link + target="$(realpath -m "$source/../$target")" + if [ "$target" = "$PWD/$deletion" ]; then + die "You broke the symlink \"$source\" -> \"$target\"" + fi + fi + done || exit + done || exit + + # TODO: also check potential symlinks pointing to now empty directories +fi