1

A common problem game developers have with the Unity3D engine and Git is that Unity and Git fight for whether an empty directory should exist or not - Git doesn't care, Unity3D will keep making git trackable *.meta files for empty directories. Basically means devs have to manually find and delete directories and meta files if a commit is made removing files and directories.

I'd like to create a Git post-checkout hook to trigger a directory removal when Git deletes files.

I have made a good start on it - I know what I need Git to do. But I'm not great with shell scripting and I'm struggling to make it work efficiently and correctly.

The main issue I'm having is that I just cannot get this line right:

dirs_to_check="($changed_files | xargs dirname | xargs sort -u)"

I was hoping it'd pipe each line into dirname, then take the whole list and remove dupes.

#!/bin/sh
# This script will be run by Git after a checkout.

# --- Command line
oldRev="$1"
newRev="$2"
isBranchCheckout="$3"

# Grab a list of deleted files:
changed_files="$(git diff-tree -r --name-only --diff-filter=D --no-commit-id $oldRev $newRev)"
# Just testing:
##changed_files="$(git diff-tree -r --name-only --no-commit-id f5865290 eb793b0c)"

# Early exit if there are no removed files at all:
if [ -z "$changed_files" ]; then
    echo "No empty dirs"
    exit 0
fi

echo "$changed_files"
# Get the list of dir paths and then sort and remove dupes: 
dirs_to_check="($changed_files | xargs dirname | xargs sort -u)"

# For each dir check if its empty and if so, remove it:
# TODO: What about the case where the parent dir is also empty of files?
for dir in $dirs_to_check; do
    if [ "$(ls -A $dir)" ]; then
        echo "$dir Not Empty"
    else
        echo "$dir Empty"
        rm $dir
    fi
done

Here is some sample changed_files text if you want to test more easily:

test/with dir spaces/debrief/css/style.css
WFTO/uiresources/wftoUI/debrief/debrief.html
WFTO/uiresources/wftoUI/debrief/debrief_specification.js
WFTO/uiresources/wftoUI/debrief/js/debrief.js
WFTO/uiresources/wftoUI/debrief/js/debrief_specification.js
WFTO/uiresources/wftoUI/loading/Loading.css
WFTO/uiresources/wftoUI/loading/Loading.html
WFTO/uiresources/wftoUI/loading/LoadingDLC1.html
WFTO/uiresources/wftoUI/loading/images/HoG-logo.png
WFTO/uiresources/wftoUI/loading/images/background_unused.jpg
WFTO/uiresources/wftoUI/loading/images/banner-patch-1_4.png
WFTO/uiresources/wftoUI/loading/images/bg-back.jpg
WFTO/uiresources/wftoUI/loading/images/bg-front_unused.jpg
WFTO/uiresources/wftoUI/loading/images/bg-front_unused.png
WFTO/uiresources/wftoUI/loading/images/bg-logo.png
WFTO/uiresources/wftoUI/loading/images/dlc1/bg-back.jpg
WFTO/uiresources/wftoUI/loading/images/dlc1/bg-logo.png
WFTO/uiresources/wftoUI/loading/images/dlc1/load-bar-empty.png
WFTO/uiresources/wftoUI/loading/images/dlc1/load-bar-full.png
WFTO/uiresources/wftoUI/loading/images/load-bar-empty.png
WFTO/uiresources/wftoUI/loading/images/load-bar-full.png
WFTO/uiresources/wftoUI/loading/images/loading-background.png
WFTO/uiresources/wftoUI/loading/images/loading-background_unused.jpg
WFTO/uiresources/wftoUI/loading/images/random_loading_pics/Ld0.png
WFTO/uiresources/wftoUI/loading/images/random_loading_pics/Ld1.png
WFTO/uiresources/wftoUI/loading/images/random_loading_pics/Ld10.png
3

You probably meant

dirs_to_check="$(echo "$changed_files" | xargs dirname | sort -u)"

A simple solution, unless you have thousands of directories, is simply to try to rmdir each directory and ignore the errors.

find . -depth -type d -exec echo rmdir --ignore-fail-on-non-empty {} + 

Change . to the top of the directories concerned. If your rmdir doesnt have the ignore option just redirect 2>/dev/null to not have the warnings.

  • Thanks I'll give your changes a go. Re find - Its really important that it is efficient, so if I can use find to check specific directories and their parents that'd be great. I can't afford to run find on the entire project each time. – S.Richmond Apr 22 '16 at 14:56
  • Damn. Either xargs or dirname cannot handle path lines with spaces in them. Maybe I need to use for and bash arrays? – S.Richmond Apr 22 '16 at 15:02
  • 1
    Add -d '\n' to xargs to cope with the spaces. Does your changed_files variable have many files per line? – meuh Apr 22 '16 at 15:09
  • Great that worked! changed_files will only have 1 my/path to/file/filename.blah per line, as per the sample posted above. Now that I have a list of empty dirs, would you have any suggestions on one would check parent dir if it too is empty of files, recursively? I will fall back to just writing a recursive function if not. – S.Richmond Apr 22 '16 at 15:19
  • 1
    You can usefully reverse your sort (sort -ur) before giving them to your for loop, as then at least an rmdir won't fail just because there is an empty subdir you have not yet removed. You can apply dirname to your list of dirs to get the parent dirs, and so on in a loop until the remaining dir is .. – meuh Apr 22 '16 at 15:25
2
find "$DIR_TO_CLEAN" -type d -empty -delete -print

Can remove -print to not get list of deleted directories.

Edit: If you do not want to remove the "$DIR_TO_CLEAN" directory itself, add -mindepth:

find "$DIR_TO_CLEAN" -mindepth 1 -type d -empty -delete -print
  • +1 I'd forgotten about these find built-ins. -delete implies -depth, so you can even remove that arg. – meuh Apr 22 '16 at 16:17
  • Can you specify what exactly this command does? Does it delete the specified directory in it does not contain any files? What about if it contains child empty directories? – S.Richmond Apr 22 '16 at 16:36
  • 1
    Read this as "find and delete all directories that are empty, depth-first search". It will delete the "$DIR_TO_CLEAN" directory if it is empty (after all other stuff deleted) - see "Edit" above. The -delete option implies -depth, so it will first delete any empty child directories, and will then delete resulting empty directories. – Dan Armstrong Apr 22 '16 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.