#!/usr/bin/env bash
#
# Clean unknown files from the working tree.
# Copyright (c) Pavel Roskin, 2005
#
# Cleans file and directories that are not under version control.
# When run without arguments, files ignored by cg-status and directories
# are not removed.
#
# OPTIONS
# -------
# -d::
#	Also clean directories and their contents.
#
# -D::
#	Same as -d but try harder (change permissions first).
#
# -n::
#	Do not actually remove the files, just pretend to do so.
#
# -q::
#	Quiet - don't report what's being cleaned.
#
# -x::
#	Also clean files ignored by cg-status, such as object files.

USAGE="cg-clean [-d] [-D] [-n] [-q] [-x]"

. ${COGITO_LIB}cg-Xlib || exit 1

noexclude=
cleandir=
cleandirhard=
quiet=
rm=rm
while optparse; do
	if optparse -d; then
		cleandir=1
	elif optparse -D; then
		cleandir=1
		cleandirhard=1
	elif optparse -n; then
		pretendrm () { echo rm "$@"; }
		rm="pretendrm"
	elif optparse -q; then
		quiet=1
	elif optparse -x; then
		noexclude=1
	else
		optfail
	fi
done

[ "$ARGS" ] && usage


clean_dirs()
{
	dirlist=$(mktemp -t gitlsfiles.XXXXXX)
	git-ls-files --cached |
		sed -n 's|/[^/]*$||p' |
		while IFS=$'\n' read dir; do
			while true; do
				echo "$dir"
				updir="${dir%/*}"
				[ "$dir" = "$updir" ] && break
				dir="$updir"
			done
		done |
		sort -u >"$dirlist"

	save_IFS="$IFS"
	IFS=$'\n'

	find ./ -type d -print |
		sed 's/^\.\///;/^$/d;/^\.git$/d;/^\.git\//d' |
		cat - "$dirlist" | sort -u |
		diff - "$dirlist" |
		sed -n 's/< //p' |
	while read dir; do
		if [ ! -d "$dir" ]; then
			# Perhaps directory was removed with its parent
			continue
		fi
		if [ -z "$cleandir" ]; then
			echo "Not removing $dir/"
			continue
		fi
		[ "$quiet" ] || echo "Removing $dir/"
		if [ "$cleandirhard" ]; then
			chmod -R 700 "$dir"
		fi
		$rm -rf "$dir"
		if [ -e "$dir" -o -L "$dir" ]; then
			echo "Cannot remove $dir/"
		fi
	done

	IFS="$save_IFS"
	rm "$dirlist"
}

clean_files()
{
	xopt=
	[ "$noexclude" ] && xopt="-x"

	save_IFS="$IFS"
	IFS=$'\n'

	cg-status -n -s '?' "$xopt" -w |
	while read file; do
		if [ -d "$file" -a ! -L "$file" ]; then
			# Sanity check, shouldn't happen
			echo "FATAL: cg-status reports directories (internal error)" >&2
			exit 1
		elif [ -e "$file" -o -L "$file" ]; then
			[ "$quiet" ] || echo "Removing $file"
			$rm -f "$file"
			# rm would complain itself on failure
		else
			echo "File $file has disappeared!"
		fi
	done

	IFS="$save_IFS"
}


cd "${_git_relpath-.}"

# Even if -d or -D is not specified, we want to tell user about
# directories that are not removed
if [ -z "$quiet" -o "$cleandir" ]; then
	clean_dirs
fi

clean_files
