Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 213 lines (185 sloc) 5.5 KB
#!/usr/bin/env ruby
# USAGE
# dot --all
# dot --all --quiet
#
# dot --fixable_links
# dot --other_links
# dot --conflict_files
# dot --uninstalled_files
#
# dot --broken_links
# dot --broken_links --remove-broken
#
# dot --untracked_files
# dot --untracked_files --ignore-missing
#
# Global swithces
# --run # actually run the commands
# --quiet # don't echo each command
#
# TODO
# * add --add/--collect command to allow seeding of a dotfiles repository
# * allow to put back a file from the repo and remove it from the repo and add it to ignore
# * allow reverse switches
# * remove horrible duplications
# * add switch to only list files, not whole commands
# * add switch to limit action to just a subsection of the tracked dotfiles
# * add --trace to allow tracing of a file through the conditions
# * add interactive processing of files
# * more decent ignores
require 'pathname'
require 'fileutils'
$ADD_MISSING = !ARGV.find{|x| x == '--ignore-missing'}
$REMOVE_BROKEN = ARGV.find{|x| x == '--remove-broken'}
$DRYRUN = !ARGV.find{|x| x == '--run'}
$DEBUG = !ARGV.find{|x| x == '--quiet'}
$ALL = ARGV.find{|x| x == '--all'}
if $ALL
$OPTIONS = {
:fixable_links => true,
:broken_links => true,
:other_links => true,
:conflict_files => true,
:untracked_files => true,
:uninstalled_files => true
}
else
$OPTIONS = {
:fixable_links => ARGV.find{|x| x == '--fixable_links'},
:broken_links => ARGV.find{|x| x == '--broken_links'},
:other_links => ARGV.find{|x| x == '--other_links'},
:conflict_files => ARGV.find{|x| x == '--conflict_files'},
:untracked_files => ARGV.find{|x| x == '--untracked_files'},
:uninstalled_files => ARGV.find{|x| x == '--uninstalled_files'}
}
end
$HOME = File.expand_path(ENV['HOME'])
$DOTFILES = File.expand_path(File.dirname(__FILE__))
def exec(cmds, force=nil)
cmds = [cmds] if cmds.is_a? String
cmds.each do |cmd|
puts cmd if $DEBUG
`#{cmd}` if force || !$DRYRUN
end
end
class String
def in_home
File.join($HOME, self)
end
def in_git
File.join($DOTFILES, self)
end
def real
Pathname.new(self).readlink.to_s
end
end
IGNORES = File.readlines(File.join($DOTFILES, ".ignores")).map do |ignore|
File.join(ignore.chomp)
end
FileUtils.cd $DOTFILES
files = []
# all tracked files
cmd = 'find -d . -type f ! -path "*.git/*" ! -path "./dot*" | grep -v -e "\.$" | sed -e "s/^\.\///"'
files = `#{cmd}`
# all files (not dirs) ~/.*
cmd = 'find ~/.* -type f -maxdepth 0 | grep -v "\.$" | grep -v -e "\.$" | sed -e "s/.*\///"'
files << `#{cmd}`
# all tracked dirs
cmd = 'find -d . -type d -maxdepth 1 ! -path "*/.git" | grep -v -e "\.$" | sed -e "s/^\.\///"'
dirs = `#{cmd}`.split("\n")
# all possibly untracked files (all files in tracked directories)
dirs.each do |dir|
next if IGNORES.find{ |i| i[-1,1] == "/" ? "#{dir}/".index(i) == 0 : "#{dir}/" == i}
cmd = 'find -d ~/' + dir + ' -type f -o -type l | grep -v -e "\.$" | sed -e "s/^'+$HOME.gsub("/", '\\/')+'\///"'
files << `#{cmd}`
end
fixable_links = []
broken_links = []
other_links = []
conflict_files = []
untracked_files = []
uninstalled_files = []
files.split("\n").uniq.each do |file|
next if IGNORES.find{ |i| i[-1,1] == "/" ? file.index(i) == 0 : (file == i || File.basename(file) == i)}
next if file =~ /.DS_Store/
cmds = []
case
when File.symlink?(file.in_home)
if file.in_home.real == file.in_git
unless File.exists? file.in_git
broken_links << file
end
else
if File.exists? file.in_git
fixable_links << file
else
if file.in_home.real.index($DOTFILES) == 0 || file.in_home.real.index("dotfiles")
broken_links << file
else
other_links << file
end
end
end
when File.file?(file.in_home)
if File.exists?(file.in_git)
conflict_files << file
else
untracked_files << file
end
when !File.exists?(file.in_home)
uninstalled_files << file
when File.directory?(file.in_home)
end
end
if $OPTIONS[:fixable_links]
puts "***** fixable links: #{fixable_links.length}"
fixable_links.each do |file|
exec "ln -s -f -v '#{file.in_git}' '#{file.in_home}'", true
end
end
if $OPTIONS[:broken_links]
puts "***** broken links: #{broken_links.length}"
broken_links.each do |file|
if $REMOVE_BROKEN
exec "rm '#{file.in_home}'"
else
exec "echo '#{file.in_home}'"
end
end
end
if $OPTIONS[:other_links]
puts "***** other links: #{other_links.length}"
other_links.each do |file|
exec "echo '#{file.in_home}'@ -\\> '#{file.in_home.real}'"
end
end
if $OPTIONS[:conflict_files]
puts "***** conflict files: #{conflict_files.length}"
conflict_files.each do |file|
exec "echo '!#{file.in_home}'"
end
end
if $OPTIONS[:untracked_files]
puts "***** untracked files: #{untracked_files.length}"
untracked_files.each do |file|
if $ADD_MISSING
cmds = []
cmds << "mkdir -p '#{File.dirname(file.in_git)}'"
cmds << "mv '#{file.in_home}' '#{file.in_git}'"
cmds << "ln -s -f -v '#{file.in_git}' '#{file.in_home}'"
else
cmds = "echo '#{file}' >> #{$DOTFILES}/.ignores"
end
exec cmds
end
end
if $OPTIONS[:uninstalled_files]
puts "***** uninstalled files: #{uninstalled_files.length}"
uninstalled_files.each do |file|
cmds = []
cmds << "mkdir -p '#{File.dirname(file.in_home)}'"
cmds << "ln -s -f -v '#{file.in_git}' '#{file.in_home}'"
exec cmds
end
end