| #!/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 |
| |
| |
| |