From 7f834277493eead92647e7ba32105133c144c01b Mon Sep 17 00:00:00 2001 From: Julian Prein Date: Mon, 15 Sep 2025 17:40:35 +0200 Subject: [PATCH] hooks:pre-commit: Broken link detection on delete Until now the hook only checked newly added symlinks. This patch is a first draft of also checking the worktree and index for any dangling symlinks after staging a deletion. The whole thing probably breaks when file-names contain newlines and maybe also a mix of quotes. I plan on making this more robust in the future but see no urgency for it since this repository has pretty simple filenames. --- meta/git/hooks/pre-commit | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) 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