rscm 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
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