rscm 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,11 +1,21 @@
1
1
  = RSCM Changelog
2
2
 
3
+ == Version 0.3.5
4
+
5
+ This release adds new API methods for accessing files and their revisions and contents.
6
+
7
+ * Added new scm.history_file method (returning RSCM::HistoryFile).
8
+ * Added RSCM::HistoryFile.revision_files methods to get all revisions of a file.
9
+ * Added RSCM::RevisionFile.open to get the contents of a specific revision of a file.
10
+ * Set defaults for Subversion username/password
11
+ * Removed logging code (may reintroduce later if I come up with a better way to configure it)
12
+
3
13
  == Version 0.3.4
4
14
 
5
15
  This release fixes some bugs on windows.
6
16
 
7
17
  * Removed redirection to dev/null for CVS commands. 2>nul didn't seem to work on windows.
8
- * Fixed a the path to touch.exe
18
+ * Fixed the path to touch.exe (needed in tests)
9
19
 
10
20
  == Version 0.3.3
11
21
 
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = RSCM - Ruby Source Control Management (0.3.4)
1
+ = RSCM - Ruby Source Control Management (0.3.5)
2
2
 
3
3
  RSCM is to SCM what DBI/JDBC/ODBC are to databases - an SCM-independent API for accessing different SCMs. The features are roughly:
4
4
 
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ require 'meta_project'
10
10
 
11
11
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
12
12
  PKG_NAME = 'rscm'
13
- PKG_VERSION = '0.3.4' + PKG_BUILD
13
+ PKG_VERSION = '0.3.5' + PKG_BUILD
14
14
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
15
 
16
16
  desc "Default Task"
@@ -134,7 +134,8 @@ task :release_files => [:gem] do
134
134
  end
135
135
  end
136
136
 
137
- task :publish_doc => [:rdoc] do
137
+ desc "Publish docs/website"
138
+ task :publish_doc do
138
139
  publisher = Rake::RubyForgePublisher.new(PKG_NAME, ENV['RUBYFORGE_USER'])
139
140
  publisher.upload
140
141
  end
data/lib/rscm/base.rb CHANGED
@@ -20,6 +20,7 @@ module RSCM
20
20
  # * move
21
21
  # * revisions
22
22
  # * uptodate?
23
+ # * file
23
24
  #
24
25
  # In addition to operations related to working copies, the same instance should provide
25
26
  # methods to administer the working copy's associated 'central' repository. These are:
@@ -77,8 +78,14 @@ module RSCM
77
78
 
78
79
  public
79
80
 
80
- attr_accessor :checkout_dir
81
+ def checkout_dir=(dir)
82
+ @checkout_dir = PathConverter.filepath_to_nativepath(dir, false)
83
+ end
81
84
 
85
+ def checkout_dir
86
+ @checkout_dir
87
+ end
88
+
82
89
  def to_yaml_properties
83
90
  props = instance_variables
84
91
  props.delete("@checkout_dir")
@@ -144,7 +151,7 @@ module RSCM
144
151
  # Commit (check in) modified files.
145
152
  def commit(message)
146
153
  end
147
-
154
+
148
155
  # Checks out or updates contents from a central SCM to +checkout_dir+ - a local working copy.
149
156
  # If this is a distributed SCM, this method should create a 'working copy' repository
150
157
  # if one doesn't already exist. Then the contents of the central SCM should be pulled into
@@ -186,9 +193,10 @@ module RSCM
186
193
  end
187
194
 
188
195
  # Returns a Revisions object for the period specified by +from_identifier+ (exclusive, i.e. after)
189
- # and +to_identifier+ (inclusive).
196
+ # and +to_identifier+ (inclusive). If +relative_path+ is specified, the result will only contain
197
+ # revisions pertaining to that path.
190
198
  #
191
- def revisions(from_identifier, to_identifier=Time.infinity)
199
+ def revisions(from_identifier, to_identifier=Time.infinity, relative_path=nil)
192
200
  # Should be overridden by subclasses
193
201
  revisions = Revisions.new
194
202
  revisions.add(
@@ -206,6 +214,11 @@ module RSCM
206
214
  )
207
215
  revisions
208
216
  end
217
+
218
+ # Returns a HistoricFile for +relative_path+
219
+ def file(relative_path)
220
+ HistoricFile.new(relative_path, self)
221
+ end
209
222
 
210
223
  # Whether the working copy is in synch with the central
211
224
  # repository's revision/time identified by +identifier+.
data/lib/rscm/better.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  module RSCM
2
2
  class Better
3
- @@logger = nil
4
- @@logger = RSCM_DEFAULT_LOGGER
5
-
6
3
  def self.popen(cmd, mode="r", expected_exit=0, &proc)
7
- @@logger.info "Executing command: '#{cmd}'" if @@logger
8
4
  ret = IO.popen(cmd, mode) do |io|
9
5
  proc.call(io)
10
6
  end
@@ -0,0 +1,22 @@
1
+ module RSCM
2
+ # Represents the full history of a single file
3
+ class HistoricFile
4
+ def initialize(relative_path, scm)
5
+ @relative_path, @scm = relative_path, scm
6
+ end
7
+
8
+ # Returns an Array of RevisionFile - from Time.epoch until Time.infinity (now)
9
+ def revision_files
10
+ @scm.revisions(Time.epoch, Time.infinity, @relative_path).collect do |revision|
11
+ if revision.files.length != 1
12
+ files_s = revision.files.collect{|f| f.to_s}.join("\n")
13
+ raise "The file-specific revision didn't have exactly one file, but #{revision.files.length}:\n#{files_s}"
14
+ end
15
+ if(revision.files[0].path != @relative_path)
16
+ raise "The file-specific revision didn't have expected path #{@relative_path}, but #{revision.files[0].path}"
17
+ end
18
+ revision.files[0]
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/rscm/logging.rb CHANGED
@@ -6,14 +6,3 @@ if(WINDOWS)
6
6
  else
7
7
  HOMEDIR = ENV['HOME']
8
8
  end
9
-
10
- begin
11
- RSCM_DEFAULT_LOGGER = Logger.new("#{HOMEDIR}/.rscm.log")
12
- rescue StandardError
13
- RSCM_DEFAULT_LOGGER = Logger.new(STDERR)
14
- RSCM_DEFAULT_LOGGER.level = Logger::WARN
15
- RSCM_DEFAULT_LOGGER.warn(
16
- "RSCM Error: Unable to access log file. Please ensure that #{HOMEDIR}/.rscm.log exists and is chmod 0666. " +
17
- "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
18
- )
19
- end
@@ -20,10 +20,17 @@ module RSCM
20
20
  attr_accessor :message
21
21
  # This is a UTC ruby time
22
22
  attr_accessor :time
23
+ attr_accessor :scm
23
24
 
24
25
  def initialize(path=nil, status=nil, developer=nil, message=nil, native_revision_identifier=nil, time=nil)
25
26
  @path, @developer, @message, @native_revision_identifier, @time, @status = path, developer, message, native_revision_identifier, time, status
26
27
  end
28
+
29
+ # Returns/yields an IO containing the contents of this file, using +scm+ this
30
+ # file lives in.
31
+ def open(scm, &block)
32
+ scm.open(self, &block)
33
+ end
27
34
 
28
35
  def accept(visitor)
29
36
  visitor.visit_file(self)
data/lib/rscm/scm/cvs.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'stringio'
1
2
  require 'rscm/base'
2
3
  require 'rscm/path_converter'
3
4
  require 'rscm/line_editor'
@@ -91,9 +92,9 @@ module RSCM
91
92
  files.empty?
92
93
  end
93
94
 
94
- def revisions(from_identifier, to_identifier=Time.infinity)
95
+ def revisions(from_identifier, to_identifier=Time.infinity, relative_path=nil)
95
96
  checkout(to_identifier) unless uptodate?(to_identifier) # must checkout to get revisions
96
- parse_log(changes_command(from_identifier, to_identifier))
97
+ parse_log(changes_command(from_identifier, to_identifier, relative_path))
97
98
  end
98
99
 
99
100
  def diff(change)
@@ -112,6 +113,15 @@ module RSCM
112
113
  end
113
114
  end
114
115
 
116
+ def open(revision_file, &block)
117
+ with_working_dir(@checkout_dir) do
118
+ diff_cmd = "cvs -Q update -p -r #{revision_file.native_revision_identifier} #{revision_file.path}"
119
+ Better.popen(diff_cmd) do |io|
120
+ block.call io
121
+ end
122
+ end
123
+ end
124
+
115
125
  def apply_label(label)
116
126
  cvs(@checkout_dir, "tag -c #{label}")
117
127
  end
@@ -241,10 +251,10 @@ module RSCM
241
251
  revisions
242
252
  end
243
253
 
244
- def changes_command(from_identifier, to_identifier)
254
+ def changes_command(from_identifier, to_identifier, relative_path)
245
255
  # https://www.cvshome.org/docs/manual/cvs-1.11.17/cvs_16.html#SEC144
246
256
  # -N => Suppress the header if no RevisionFiles are selected.
247
- "log #{branch_option} -N #{period_option(from_identifier, to_identifier)}"
257
+ "log #{branch_option} -N #{period_option(from_identifier, to_identifier)} #{relative_path}"
248
258
  end
249
259
 
250
260
  def branch_specified?
@@ -20,6 +20,10 @@ module RSCM
20
20
  ann :tip => "If you use ssh, specify the URL as svn+ssh://username@server/path/to/repo"
21
21
  attr_accessor :url
22
22
 
23
+ ann :description => "Path"
24
+ ann :tip => "You only need to specify this if you want to be able to automatically create the repository. This should be the relative path from the start of the repository <br>to the end of the URL. For example, if your URL is <br>svn://your.server/path/to/repository/path/within/repository <br>then this value should be path/within/repository."
25
+ attr_accessor :path
26
+
23
27
  ann :description => "Username"
24
28
  ann :tip => "This only applies to svn:// URLs."
25
29
  attr_accessor :username
@@ -28,12 +32,14 @@ module RSCM
28
32
  ann :tip => "This only applies to svn:// URLs."
29
33
  attr_accessor :password
30
34
 
31
- ann :description => "Path"
32
- ann :tip => "You only need to specify this if you want to be able to automatically create the repository. This should be the relative path from the start of the repository <br>to the end of the URL. For example, if your URL is <br>svn://your.server/path/to/repository/path/within/repository <br>then this value should be path/within/repository."
33
- attr_accessor :path
34
-
35
35
  def initialize(url="", path="")
36
36
  @url, @path = url, path
37
+ @username = ""
38
+ @password = ""
39
+ end
40
+
41
+ def to_yaml_properties
42
+ ["@url", "@password", "@username", "@password"]
37
43
  end
38
44
 
39
45
  def add(relative_filename)
@@ -70,10 +76,13 @@ module RSCM
70
76
  absolute_path = PathConverter.nativepath_to_filepath(native_absolute_path)
71
77
  if(File.exist?(absolute_path) && !File.directory?(absolute_path))
72
78
  native_checkout_dir = PathConverter.filepath_to_nativepath(@checkout_dir, false)
73
- # relative_path = native_absolute_path[native_checkout_dir.length+1..-1].chomp
74
- # not sure about this old code - this seems to work. keep it here till it's verified on win32
75
- relative_path = absolute_path
76
- relative_path = relative_path.gsub(/\\/, "/") if WINDOWS
79
+ relative_path = nil
80
+ if WINDOWS
81
+ relative_path = native_absolute_path[native_checkout_dir.length+1..-1].chomp
82
+ relative_path = relative_path.gsub(/\\/, "/")
83
+ else
84
+ relative_path = absolute_path
85
+ end
77
86
  checked_out_files << relative_path
78
87
  yield relative_path if block_given?
79
88
  end
@@ -83,14 +92,6 @@ module RSCM
83
92
  checked_out_files
84
93
  end
85
94
 
86
- def checkout_commandline
87
- "svn checkout #{revision_option(nil)}"
88
- end
89
-
90
- def update_commandline
91
- "svn update #{url} #{checkout_dir}"
92
- end
93
-
94
95
  def uptodate?(identifier)
95
96
  if(!checked_out?)
96
97
  false
@@ -112,12 +113,18 @@ module RSCM
112
113
 
113
114
  def diff(file, &block)
114
115
  with_working_dir(@checkout_dir) do
115
- cmd = "svn diff -r #{file.previous_native_revision_identifier}:#{file.native_revision_identifier} \"#{url}/#{file.path}\""
116
+ cmd = "svn diff --revision #{file.previous_native_revision_identifier}:#{file.native_revision_identifier} \"#{url}/#{file.path}\""
116
117
  Better.popen(cmd) do |io|
117
118
  return(yield(io))
118
119
  end
119
120
  end
120
121
  end
122
+
123
+ def open(revision_file, &block)
124
+ Better.popen("svn cat --revision #{revision_file.native_revision_identifier} #{url}/#{revision_file.path}") do |io|
125
+ return(yield(io))
126
+ end
127
+ end
121
128
 
122
129
  def can_create_central?
123
130
  local?
@@ -190,13 +197,13 @@ module RSCM
190
197
  svn(dir, import_cmd)
191
198
  end
192
199
 
193
- def revisions(from_identifier, to_identifier=Time.infinity)
200
+ def revisions(from_identifier, to_identifier=Time.infinity, relative_path="")
194
201
  # Return empty revision if the requested revision doesn't exist yet.
195
202
  return Revisions.new if(from_identifier.is_a?(Integer) && head_revision_identifier <= from_identifier)
196
203
 
197
204
  checkout_dir = PathConverter.filepath_to_nativepath(@checkout_dir, false)
198
205
  revisions = nil
199
- command = "svn #{changes_command(from_identifier, to_identifier)}"
206
+ command = "svn #{changes_command(from_identifier, to_identifier, relative_path)}"
200
207
 
201
208
  with_working_dir(@checkout_dir) do
202
209
  Better.popen(command) do |stdout|
@@ -300,28 +307,29 @@ module RSCM
300
307
  end
301
308
 
302
309
  def checkout_command(to_identifier)
303
- checkout_dir = "\"#{checkout_dir}\""
304
- "checkout #{login_options} #{url} #{checkout_dir} #{revision_option(nil,to_identifier)}"
310
+ cd = "\"#{checkout_dir}\""
311
+ raise "checkout_dir not set" if cd == ""
312
+ "checkout #{login_options} #{url} #{cd} #{revision_option(nil,to_identifier)}"
305
313
  end
306
314
 
307
315
  def update_command(to_identifier)
308
316
  "update #{login_options} #{revision_option(nil,to_identifier)}"
309
317
  end
310
318
 
311
- def changes_command(from_identifier, to_identifier)
319
+ def changes_command(from_identifier, to_identifier, relative_path)
312
320
  # http://svnbook.red-bean.com/svnbook-1.1/svn-book.html#svn-ch-3-sect-3.3
313
321
  # file_list = files.join('\n')
314
- # WEIRD cygwin bug garbles this!?!?!?!
315
- cmd = "log --verbose #{login_options} #{revision_option(from_identifier, to_identifier)} #{url}"
322
+ full_url = relative_path ? "#{url}/#{relative_path}" : ""
323
+ cmd = "log --verbose #{login_options} #{revision_option(from_identifier, to_identifier)} #{full_url}"
316
324
  cmd
317
325
  end
318
326
 
319
327
  def login_options
320
328
  result = ""
321
- u = @username ? @username.strip : nil
322
- p = @password ? @password.strip : nil
323
- result << "--username #{u} " if u
324
- result << "--password #{p} " if p
329
+ u = @username ? @username.strip : ""
330
+ p = @password ? @password.strip : "
331
+ result << "--username #{u} " unless u == ""
332
+ result << "--password #{p} " unless p == ""
325
333
  result
326
334
  end
327
335
 
data/lib/rscm.rb CHANGED
@@ -6,4 +6,5 @@ require 'rscm/better'
6
6
  require 'rscm/base'
7
7
  require 'rscm/revision'
8
8
  require 'rscm/revision_file'
9
+ require 'rscm/historic_file'
9
10
  require 'rscm/time_ext'
@@ -10,7 +10,12 @@ module RSCM
10
10
 
11
11
  def teardown
12
12
  if @scm
13
- # @scm.destroy_central
13
+ begin
14
+ @scm.destroy_centra
15
+ rescue => e
16
+ # Fails on windows with TortoiseCVS' cvs because of resident cvslock.exe
17
+ STDERR.puts "Couldn't destroy central #{@scm.class.name}"
18
+ end
14
19
  end
15
20
  end
16
21
 
@@ -252,7 +257,7 @@ module RSCM
252
257
  +five six
253
258
  EOF
254
259
 
255
- def test_diffs
260
+ def test_diffs_and_historic_file
256
261
  work_dir = RSCM.new_temp_dir("diff")
257
262
  checkout_dir = "#{work_dir}/checkout"
258
263
  repository_dir = "#{work_dir}/repository"
@@ -302,6 +307,14 @@ EOF
302
307
  assert_match(/^\+five six/, diff_string)
303
308
  end
304
309
  assert(got_diff)
310
+
311
+ # TODO: make separate test. Make helper method for the cumbersome setup!
312
+ historic_afile = scm.file("afile.txt")
313
+ revision_files = historic_afile.revision_files
314
+ assert_equal(Array, revision_files.class)
315
+ assert(revision_files.length >= 2)
316
+ assert(revision_files.length <= 3)
317
+ assert_equal("one two three four\nfive six\n", revision_files[-1].open(scm){|io| io.read})
305
318
  end
306
319
 
307
320
  private
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rscm
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.4
7
- date: 2005-09-18 00:00:00 -04:00
6
+ version: 0.3.5
7
+ date: 2005-10-05 00:00:00 -04:00
8
8
  summary: "RSCM - Ruby Source Control Management"
9
9
  require_paths:
10
10
  - lib
@@ -39,6 +39,7 @@ files:
39
39
  - lib/rscm/base.rb
40
40
  - lib/rscm/better.rb
41
41
  - lib/rscm/difftool.rb
42
+ - lib/rscm/historic_file.rb
42
43
  - lib/rscm/line_editor.rb
43
44
  - lib/rscm/logging.rb
44
45
  - lib/rscm/mockit.rb