blob: e1858b321de8e738a67966fbcb6d99abbd61cbea [file] [log] [blame]
#!/usr/bin/ruby
# Used to prepare a directory for commit to Subversion. This is necessary for certain file types on Mac OS X because what appear to be files in the Finder
# are actually directories (Mac uses the term "bundle" for this concept). It is useless to put the .svn folder inside such a directory, because it will
# tend to be deleted whenever the "file" is saved.
#
# Instead, we want to compress the directory to a single archive file; the bundle will be marked as svn:ignore.
#
# We use tar with Bzip2 compression, which is resource intensive to create, but compresses much better than GZip or PKZip.
#
# The trick is that we only want to create the archive version when necessary; when the archive does not exist, or when any file
# in the bundle is newer than the archive.
require 'optparse'
# Set via command line options
$extensions = %w{pages key oo3 graffle}
$recursive = true
$dry_run = false
# Queue of folders to search (for bundles)
$queue = []
def matching_extension(name)
dotx = name.rindex('.')
return false unless dotx
ext = name[dotx + 1 .. -1]
return $extensions.include?(ext)
end
# Iterate over the directory, identify bundles that may need to be compressed and (if recursive) subdirectories
# to search.
#
# path: string path for a directory
def search_directory(dirpath)
Dir.foreach(dirpath) do |name|
# Skip hidden files and directories
next if name[0..0] == "."
path = File.join(dirpath, name)
next unless File.directory?(path)
if matching_extension(name)
update_archive path
next
end
if $recursive
$queue << path
end
end
end
def needs_update(bundle_path, archive_path)
return true unless File.exists?(archive_path)
archive_mtime = File.mtime(archive_path)
# The archive exists ... can we find a file inside the bundle thats newer?
# This won't catch deletions, but that's ok. Bundles tend to get completly
# overwritten when any tiny thing changes.
dirqueue = [bundle_path]
until dirqueue.empty?
dirpath = dirqueue.pop
Dir.foreach(dirpath) do |name|
path = File.join(dirpath, name)
if File.directory?(path)
dirqueue << path unless [".", ".."].include?(name)
next
end
# Is this file newer?
if File.mtime(path) > archive_mtime
return true
end
end
end
return false
end
def update_archive(path)
archive = path + ".tar.bz2"
return unless needs_update(path, archive)
if $dry_run
puts "Would create #{archive}"
return
end
puts "Creating #{archive}"
dir = File.dirname(path)
bundle = File.basename(path)
# Could probably fork and do it in a subshell
system "tar --create --file=#{archive} --bzip2 --directory=#{dir} #{bundle}"
end
$opts = OptionParser.new do |opts|
opts.banner = "Usage: prepsvn [options]"
opts.on("-d", "--dir DIR", "Add directory to search (if no directory specify, current directory is searched)") do |value|
$queue << value
end
opts.on("-e", "--extension EXTENSION", "Add another extension to match when searching for bundles to archive") do |value|
$extensions << value
end
opts.on("-N", "--non-recursive", "Do not search non-bundle sub directories for files to archive") do
$recursive = false
end
opts.on("-D", "--dry-run", "Identify what archives would be created") do
$dry_run = true
end
opts.on("-h", "--help", "Help for this command") do
puts opts
exit
end
end
def fail(message)
puts "Error: #{message}"
puts $opts
end
begin
$opts.parse!
rescue OptionParser::InvalidOption
fail $!
end
# If no --dir specified, use the current directory.
if $queue.empty?
$queue << Dir.getwd
end
until $queue.empty?
search_directory $queue.pop
end