lib/svn-command/subversion.rb in svn-command-0.1.1 vs lib/svn-command/subversion.rb in svn-command-0.2.1
- old
+ new
@@ -19,10 +19,16 @@
#require 'active_support/core_ext/module/attribute_accessors'
#require 'facets/core/class/cattr'
gem 'qualitysmith_extensions'
require 'qualitysmith_extensions/module/attribute_accessors'
+# RSCM is used for some of the abstraction, such as for parsing log messages into nice data structures. It seems like overkill, though, to use RSCM for most things...
+gem 'rscm'
+#require 'rscm'
+#require 'rscm/scm/subversion'
+require 'rscm/scm/subversion_log_parser'
+
# Wraps the Subversion shell commands for Ruby.
module Subversion
# True if you want output from svn to be colorized (useful if output is for human eyes, but not useful if using the output programatically)
@@color = false
mattr_accessor :color
@@ -135,10 +141,13 @@
def self.update(*args)
args = ['./'] if args.empty?
execute("update #{args.join ' '}")
end
+ # The output from `svn status` is nicely divided into two "sections": the section which pertains to the current working copy (not
+ # counting externals as part of the working copy) and then the section with status of all of the externals.
+ # This method returns the first section.
def self.status_the_section_before_externals(path = './')
status = status(path) || ''
status.sub!(/(Performing status.*)/m, '')
end
@@ -195,65 +204,119 @@
# Set the property
puts "About to set propety to: #{lines.inspect}" if $debug
self.set_property property, lines.join("\n"), path
end
+ # :todo: Stop assuming the svn: namespace. What's the point of a namespace if you only allow one of them?
def self.get_property(property, path = './')
execute "propget svn:#{property} #{path}"
end
+ def self.get_revision_property(property_name, rev)
+ execute("propget --revprop #{property_name} -r #{rev}").chomp
+ end
+
def self.delete_property(property, path = './')
execute "propdel svn:#{property} #{path}"
end
+ def self.delete_revision_property(property_name, rev)
+ execute("propdel --revprop #{property_name} -r #{rev}").chomp
+ end
+
def self.set_property(property, value, path = './')
execute "propset svn:#{property} '#{value}' #{path}"
end
+ def self.set_revision_property(property_name, rev)
+ execute("propset --revprop #{property_name} -r #{rev}").chomp
+ end
+ # Gets raw output of proplist command
+ def self.proplist(rev)
+ execute("proplist --revprop -r #{rev}")
+ end
+ # Returns an array of the names of all revision properties currently set on the given +rev+
+ # Tested by: ../../test/subversion_test.rb:test_revision_properties_names
+ def self.revision_properties_names(rev)
+ raw_list = proplist(rev)
+ raw_list.scan(/^ +([^ ]+)$/).map { |matches|
+ matches.first.chomp
+ }
+ end
+ # Returns an array of RevisionProperty objects (name, value) for revisions currently set on the given +rev+
+ # Tested by: ../../test/subversion_test.rb:test_revision_properties
+ def self.revision_properties(rev)
+ revision_properties_names(rev).map { |property_name|
+ RevisionProperty.new(property_name, get_revision_property(property_name, rev))
+ }
+ end
+
def self.make_directory(dir)
execute "mkdir #{dir}"
end
def self.help(*args)
execute "help #{args.join(' ')}"
end
+ # Returns the raw output from svn log
def self.log(*args)
args = ['./'] if args.empty?
execute "log #{args.join(' ')}"
end
+ # Returns the revision number for head.
def self.latest_revision(*args)
args = ['./'] if args.empty?
matches = /Status against revision:\s+(\d+)/m.match(status_against_server(args))
matches && matches[1]
end
+ # Returns an array of RSCM::Revision objects
+ def self.revisions(*args)
+ # Tried using this, but it seems to expect you to pass in a starting date or accept the default starting date of right now, which is silly if you actually just want *all* revisions...
+ #@rscm = ::RSCM::Subversion.new
+ #@rscm.revisions
+
+ #log_output = Subversion.log('-v')
+ log_output = Subversion.log(*(['-v'] + args))
+ parser = ::RSCM::SubversionLogParser.new(io = StringIO.new(log_output), url = 'http://ignore.me.com')
+ revisions = parser.parse_revisions
+ revisions
+ end
+
+
def self.info(*args)
args = ['./'] if args.empty?
execute "info #{args.join(' ')}"
end
# :todo: needs some serious unit-testing love
def self.base_url(path_or_url = './')
- base_url = nil # needed so that base_url variable isn't local to if block!
- started_using_dot_dots = false
- loop do
- matches = /URL: (.+)/.match(info(path_or_url))
- if matches && matches[1]
- base_url = matches[1]
- else
- break base_url
- end
+ matches = info(path_or_url).match(/^Repository Root: (.+)/)
+ matches && matches[1]
- # Keep going up the path, one directory at a time, until `svn info` no longer returns a URL (will probably eventually return 'svn: PROPFIND request failed')
- if path_or_url.include?('/') && !started_using_dot_dots
- path_or_url = File.dirname(path_or_url)
- else
- started_using_dot_dots = true
- path_or_url = File.join(path_or_url, '..')
- end
- #puts 'going up to ' + path_or_url
- end
+ # It appears that we might need to use this old way (which looks at 'URL'), since there is actually a
+# base_url = nil # needed so that base_url variable isn't local to loop block (and reset during next iteration)!
+# started_using_dot_dots = false
+# loop do
+# matches = /^URL: (.+)/.match(info(path_or_url))
+# if matches && matches[1]
+# base_url = matches[1]
+# else
+# break base_url
+# end
+#
+# # Keep going up the path, one directory at a time, until `svn info` no longer returns a URL (will probably eventually return 'svn: PROPFIND request failed')
+# if path_or_url.include?('/') && !started_using_dot_dots
+# path_or_url = File.dirname(path_or_url)
+# else
+# started_using_dot_dots = true
+# path_or_url = File.join(path_or_url, '..')
+# end
+# #puts 'going up to ' + path_or_url
+# end
end
+ def self.root_url(*args); base_url(*args); end
+ def self.repository_root(*args); base_url(*args); end
# The location of the executable to be used
def self.executable
@@executable ||=
ENV['PATH'].split(':').each do |dir|
@@ -292,15 +355,21 @@
p command
end
valid_options = [:capture, :exec, :popen]
case method
+
when :capture
`#{command} 2>&1`
+
when :exec
#Kernel.exec *args
Kernel.exec command
+
+ when :system
+ Kernel.system command
+
when :popen
# This is just an idea of how maybe we could improve the LATENCY. Rather than waiting until the command completes
# (which can take quite a while for svn status sometimes since it has to walk the entire directory tree), why not process
# the output from /usr/bin/svn *in real-time*??
#
@@ -336,11 +405,15 @@
+#Subversion.const_set(:RevisionProperty) = Struct.new(:name, :repository_path)
module Subversion
+
+ RevisionProperty = Struct.new(:name, :value)
+
# Represents an "externals container", which is a directory that has the <tt>svn:externals</tt> property set to something useful.
# Each ExternalsContainer contains a set of "entries", which are the actual directories listed in the <tt>svn:externals</tt>
# property and are "pulled into" the directory.
class ExternalsContainer
ExternalItem = Struct.new(:name, :repository_path)