def switch(dir, new_repos)
return unless File.directory?(dir)
return skip(dir, "locked") unless svn(:propget, LOCKED, dir) == ''
status = svn(:status, '--show-updates', dir)
new_local_rev = nil
new_status = Array.new
status.each_line do |line|
if line =~ /status.+\s(\d+)$/i then
new_local_rev = $1.to_i
else
new_status << line unless line =~ /^\?/
end
end
raise "Unable to parse status\n#{status}" unless new_local_rev
return skip(dir, "pending updates -- run \"svn update #{dir}\"\n#{new_status}") if new_status.size > 0
logging_stream.puts "Processing '#{dir}'..."
repos = svn(:propget, Piston::ROOT, dir).chomp
uuid = svn(:propget, Piston::UUID, dir).chomp
remote_revision = svn(:propget, Piston::REMOTE_REV, dir).chomp.to_i
local_revision = svn(:propget, Piston::LOCAL_REV, dir).chomp.to_i
local_revision = local_revision.succ
new_info = YAML::load(svn(:info, new_repos))
raise Piston::CommandError, "Switching repositories is not supported at this time\nYou initially imported from #{uuid}, but are now importing from #{new_info['Repository UUID']}" unless uuid == new_info['Repository UUID']
logging_stream.puts " Fetching remote repository's latest revision and UUID"
info = YAML::load(svn(:info, "#{repos}@#{remote_revision}"))
return skip(dir, "Repository UUID changed\n Expected #{uuid}\n Found #{info['Repository UUID']}\n Repository: #{repos}") unless uuid == info['Repository UUID']
new_remote_rev = new_info['Last Changed Rev'].to_i
revisions = (remote_revision .. (revision || new_remote_rev))
logging_stream.puts " Restoring remote repository to known state at r#{revisions.first}"
svn :checkout, '--ignore-externals', '--quiet', '--revision', revisions.first, "#{repos}@#{remote_revision}", dir.tmp
logging_stream.puts " Updating remote repository to #{new_repos}@#{revisions.last}"
updates = svn :switch, '--revision', revisions.last, new_repos, dir.tmp
logging_stream.puts " Processing adds/deletes"
merges = Array.new
changes = 0
updates.each_line do |line|
next unless line =~ %r{^([A-Z]).*\s+#{Regexp.escape(dir.tmp)}[\\/](.+)$}
op, file = $1, $2
changes += 1
case op
when 'A'
if File.directory?(File.join(dir.tmp, file)) then
svn :mkdir, '--quiet', File.join(dir, file)
else
copy(dir, file)
svn :add, '--quiet', '--force', File.join(dir, file)
end
when 'D'
svn :remove, '--quiet', '--force', File.join(dir, file)
else
copy(dir, file)
merges << file
end
end
log = svn(:log, '--quiet', '--revision', (local_revision .. new_local_rev).to_svn, '--limit', '2', dir)
if local_revision < new_local_rev && log.count("\n") > 3 then
logging_stream.puts " Merging local changes back in"
merges.each do |file|
begin
svn(:merge, '--quiet', '--revision', (local_revision .. new_local_rev).to_svn,
File.join(dir, file), File.join(dir, file))
rescue RuntimeError
next if $!.message =~ /Unable to find repository location for/
end
end
end
logging_stream.puts " Removing temporary files / folders"
FileUtils.rm_rf dir.tmp
logging_stream.puts " Updating Piston properties"
svn :propset, Piston::ROOT, new_repos, dir
svn :propset, Piston::REMOTE_REV, revisions.last, dir
svn :propset, Piston::LOCAL_REV, new_local_rev, dir
svn :propset, Piston::LOCKED, revisions.last, dir if lock
logging_stream.puts " Updated to r#{revisions.last} (#{changes} changes)"
end