class Root include Sys::Client attr_reader :root_path def initialize(root_path, clean = false) @root_path = root_path @clean = clean @status = nil end class Processes < Array def initialize end def launch pid = fork do yield end self << pid end def join self.each do |pid| Process.waitpid(pid, 0) end end end def update if @clean remove_unversioned end remove_deleted_externals externals_before = all_externals svn("up --ignore-externals #{root_path}") # for some reason (array - array) doesn't work right here # so i had to write my own subtract method removed_externals = externals_before.subtract(all_externals) removed_externals.each do |ext| say "Removed external #{ext}" FileUtils.rm_rf(ext.path) end @processes = Processes.new already_up_to_date = [] all_externals.each do |external| if external.should_update? if File.exists?(external.path) update_external(external) else checkout_external(external) end else already_up_to_date << external end end unless already_up_to_date.empty? say("External#{'s' if already_up_to_date.size > 1} " + already_up_to_date.collect {|external| external.name}.join(", ") + " already up to date") end @processes.join end def update_external(external) @processes.launch do external.update end end def checkout_external(external) @processes.launch do external.checkout end end def remove_deleted_externals directories_containing_externals.each do |dir| deleted_something = false workspace_externals = External.externals_of(dir) Dir.chdir(root_path) do head_property = svn("propget -r HEAD svn:externals #{dir}", true) head_externals = External.externals_of(dir, head_property) deleted_externals = workspace_externals.select do |workspace_external| head_externals.select { |head_external| head_external.name == workspace_external.name }.empty? end unless deleted_externals.empty? deleted_externals.each do |external| # todo: check for local changes say("Deleting external #{external.path}") FileUtils.rm_rf(external.path) end # update the property first svn("up -N #{dir}") # then update all the deleted former externals svn("up " + deleted_externals.map{|external| "#{external.path}"}.join(" ")) end end if deleted_something svn("up -N #{dir}") svn("up --ignore-externals #{dir}") puts "pausing" sleep(9999) end end end def remove_unversioned status.unversioned.each do |path| if File.directory?(path) say "Removing unversioned directory #{path}" else say "Removing unversioned file #{path}" end FileUtils.rm_rf(path) end end def status @status ||= Status.new(svn("st #{root_path}", true)) end def all_externals exts = [] directories_containing_externals.collect do |parent| exts += External.externals_of(parent) end exts end def directories_containing_externals status.externals.collect do |path| if (path !~ /\//) root_path else path.gsub(/\/[^\/]*$/, '') end end.uniq end end