kill obsolete dotfiles

May 23rd, 2009

So my portable dotfiles are working out really well. There is only one fly in the ointment left. When a file has changed, it gets overwritten with a newer version, that's fine. But when a file has been renamed or removed, it will stick around in ~, creating the false impression that it's supposed to be there.

This is not a huge problem, but it does get to be tiresome when you're moving around directory hierarchies. I recently started using emacs more seriously and I expect a lot of stuff will eventually pile up in .emacs.d. Meanwhile, obsolete files and directories will clutter the hierarchy and possibly interfere with the system.

What can we do about it? The archive that holds the dotfiles is a sufficient record of what is actually in the dotfiles at any given moment. We can diff that with the files found on the system locally and pinpoint the ones that have been made obsolete.

For example, I initially had an ~/.emacs file, but then I moved it to ~/.emacs.d/init.el. So ~/.emacs is obsolete. But when I sync my dotfiles on a machine with an older version of my dotfiles, it will still have ~/.emacs around.

Not anymore. Now this happens:

dotfiles_sync

Files directly in ~ have to be listed explicitly, because we don't know anything about ~ as a whole. But files in known subdirs of ~, like ~/.emacs.d, are detected automatically.

killobsolete() {
	# files directly in ~ formerly used, now obsolete
	# subdirs of ~ checked automatically
	local suspected=".emacs"

	# init tempfiles we need to run diff on
	local found="/tmp/.myshell_found"
	local pulled="/tmp/.myshell_pulled"

	# detect files found locally
	for i in $(gunzip -c cfg.tar.gz | tar -tf -); do
		echo $(dirname "$i");
	done | grep -v '^.$' | sort | uniq | xargs find | sort | uniq | \
		grep -v ".myshell/.*_local*" > "$found"

	# detect suspected files
	for f in $(find $suspected 2>/dev/null); do
		echo "$f" >> "$found";
	done

	# sort found list
	cat "$found" | sort | uniq > "$found.x" ; mv "$found.x" "$found"

	# list files pulled from upstream
	for i in $(gunzip -c cfg.tar.gz | tar -tf -); do
		echo "$i";
		echo $(dirname "$i");
	done | grep -v '^.$' | sed "s|\/$||g" | sort | uniq > "$pulled"

	# list obsolete files
	local num=$(diff "$pulled" "$found" | grep '>' | cut -b3- | wc -l)
	if [ $num -gt 0 ]; then
		echo -e "${cyellow} ++ files found to be obsolete${creset}";
		diff "$pulled" "$found" | grep '>' | cut -b3-;

		# prompt for deletion
		echo -e "${cyellow} ++ kill obsolete? [yN]${creset}"
		read ans
		if [[ "$ans" == "y" ]]; then
			for i in $(diff "$pulled" "$found" | grep '>' | cut -b3-); do
				if [ -d "$i" ]; then
					rm -rf "$i"
				else
					rm -f "$i"
				fi
			done
		fi
	fi

	# dispose of tempfiles
	rm -f "$pulled" "$found"
}

:: random entries in this category ::