rscm 0.1.0.1338 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README +13 -28
  2. data/Rakefile +25 -24
  3. data/lib/rscm.rb +1 -6
  4. data/lib/rscm/abstract_scm.rb +36 -10
  5. data/lib/rscm/annotations.rb +50 -0
  6. data/lib/rscm/changes.rb +2 -5
  7. data/lib/rscm/logging.rb +1 -0
  8. data/lib/rscm/path_converter.rb +3 -2
  9. data/lib/rscm/{cvs → scm}/cvs.rb +16 -9
  10. data/lib/rscm/{cvs → scm}/cvs_log_parser.rb +4 -4
  11. data/lib/rscm/{darcs → scm}/darcs.rb +6 -3
  12. data/lib/rscm/scm/monotone.rb +162 -0
  13. data/lib/rscm/scm/monotone_log_parser.rb +95 -0
  14. data/lib/rscm/scm/mooky.rb +21 -0
  15. data/lib/rscm/{perforce → scm}/perforce.rb +7 -4
  16. data/lib/rscm/{starteam/starteam.rb → scm/star_team.rb} +23 -3
  17. data/lib/rscm/{svn/svn.rb → scm/subversion.rb} +17 -10
  18. data/lib/rscm/{svn/svn_log_parser.rb → scm/subversion_log_parser.rb} +8 -7
  19. data/test/rscm/abstract_scm_test.rb +21 -0
  20. data/test/rscm/annotations_test.rb +57 -0
  21. data/test/rscm/changes_fixture.rb +7 -7
  22. data/test/rscm/changes_test.rb +3 -3
  23. data/test/rscm/generic_scm_tests.rb +2 -2
  24. data/test/rscm/{cvs → scm}/cvs-dataforge.log +0 -0
  25. data/test/rscm/{cvs → scm}/cvs-test.log +0 -0
  26. data/test/rscm/{cvs → scm}/cvs_log_parser_test.rb +12 -13
  27. data/test/rscm/{cvs → scm}/cvs_test.rb +7 -7
  28. data/test/rscm/{darcs → scm}/darcs_test.rb +1 -1
  29. data/test/rscm/{monotone → scm}/keys +0 -0
  30. data/test/rscm/scm/monotone_log_parser_test.rb +109 -0
  31. data/test/rscm/{monotone → scm}/monotone_test.rb +1 -1
  32. data/test/rscm/{mooky → scm}/mooky_test.rb +1 -1
  33. data/test/rscm/{perforce → scm}/perforce_test.rb +1 -1
  34. data/test/rscm/{starteam/starteam_test.rb → scm/star_team.rb} +1 -1
  35. data/test/rscm/{svn/svn_log_parser_test.rb → scm/subversion_log_parser_test.rb} +25 -10
  36. data/test/rscm/{svn/svn_test.rb → scm/subversion_test.rb} +4 -5
  37. data/test/rscm/{svn/cargo-svn.log → scm/svn-cargo.log} +0 -0
  38. data/test/rscm/scm/svn-growl.log +875 -0
  39. data/test/rscm/scm/svn-growl2.log +30 -0
  40. data/test/rscm/{svn/proxytoys-svn.log → scm/svn-proxytoys.log} +0 -0
  41. metadata +35 -44
  42. data/lib/rscm/attr_attr.rb +0 -36
  43. data/lib/rscm/monotone/monotone.rb +0 -107
  44. data/lib/rscm/mooky/mooky.rb +0 -13
  45. data/test/actual +0 -3
  46. data/test/expected +0 -3
  47. data/test/rscm/attr_attr_test.rb +0 -32
data/README CHANGED
@@ -1,6 +1,6 @@
1
- = RSCM - Ruby Source Control Management
1
+ = RSCM - Ruby Source Control Management (0.2.0)
2
2
 
3
- RSCM is to SCM what ODBC/JDBC is to databases - a common API to access a wide variety of SCMs. The features are roughly:
3
+ RSCM is to SCM what DBI/JDBC/ODBC are to databases - an SCM-independent API for accessing a wide variety of SCMs. The features are roughly:
4
4
 
5
5
  * Check out a working copy (with possibility to specify branch/date/label)
6
6
  * Get changesets (changesets are emulated for non-transactional SCMs like CVS and StarTeam)
@@ -27,9 +27,10 @@ If you want the latest and greatest, you can get the sources, which live alongsi
27
27
 
28
28
  In progress:
29
29
 
30
+ * ClearCase -http://www-306.ibm.com/software/awdtools/clearcase (half way there)
30
31
  * Darcs - http://www.abridgegame.org/darcs (very incomplete)
31
32
  * Monotone - http://www.venge.net/monotone (half way there)
32
-
33
+
33
34
  Planned:
34
35
 
35
36
  Loads! All of them! How to add support for a new one is described further down in this file.
@@ -44,7 +45,7 @@ Here is an example of how to use RSCM to get a list of changesets from a CVS rep
44
45
 
45
46
  require 'rscm'
46
47
 
47
- scm = RSCM::SVN.new("svn://some.server/some/path/trunk", "trunk")
48
+ scm = RSCM::Subversion.new("svn://some.server/some/path/trunk", "trunk")
48
49
 
49
50
  scm.checkout("mycheckout")
50
51
  changesets = scm.changesets("mycheckout", Time.utc(2004, 11, 10, 12, 34, 22))
@@ -104,8 +105,8 @@ acceptance test suite for RSCM. By doing this you'll actually follow a TDD
104
105
  approach for your new Mooky class - except that the tests are already written
105
106
  for you!
106
107
 
107
- IMPORTANT NOTE: If your SCM doesn't provide an easy way to create new local repositories
108
- - such as with StarTeam - you're probably better off writing the tests from scratch and not
108
+ IMPORTANT NOTE: If your SCM doesn't provide an easy way to create new local repositories
109
+ (such as with StarTeam) you're probably better off writing the tests from scratch and not
109
110
  include GenericSCMTests. Instead, just make sure you have an SCM repository set up somewhere
110
111
  and write tests to work against that repository. This way you won't be able to pass the
111
112
  generic acceptance test suite, and other people (like the RSCM dev team) will probably
@@ -120,11 +121,11 @@ They're just files).
120
121
 
121
122
  Let's implement the Mooky class. Take a look at.
122
123
 
123
- lib/rscm/mooky/mooky.rb
124
+ lib/rscm/scm/mooky.rb
124
125
 
125
126
  Try running Mooky's test:
126
127
 
127
- rake test TEST=test/rscm/mooky/mooky_test.rb
128
+ rake test TEST=test/rscm/scm/mooky_test.rb
128
129
 
129
130
  Whoops - we got some failures! It failed because our checkout method returned
130
131
  nothing (nil). Let'see if we can get the a little further by implementing this
@@ -151,7 +152,7 @@ should also yield each file name as the execution proceeds).
151
152
  Once your checkout command works okay, the test will get you a little further. Just keep
152
153
  on going until all tests pass.
153
154
 
154
- NOTE: If the SCM doesn't have a command line utility or a 3rd party Ruby API, but instead
155
+ NOTE: If the SCM doesn't have a command line utility (unlikely) or a 3rd party Ruby API, but instead
155
156
  provides libraries (perhaps in C), then you should consider writing a Ruby C extension
156
157
  instead.
157
158
 
@@ -159,26 +160,10 @@ If the SCM has a Java interface, you can take the same approach as for StarTeam.
159
160
  Java classes for ChangeSets that allow easy interaction between Ruby and Java over YAML.
160
161
  You can reuse these classes for other Java based SCMs (if there are any, I don't know).
161
162
 
162
- == Writing the web interface (DamageControl only)
163
-
164
- If you're contributing a new SCM it would be nice if you took the time to implement a web interface
165
- that can be used to embed it into DamageControl.
166
-
167
- In trunk/damagecontrol, start by creating +app/views/project/_mooky.rhtml+.
168
-
169
- Create a table with 2 columns and a row for each +attr_accessor+ in the Mooky class, preferrably
170
- with some explanatory text for the users so they know how to fill it in. See how the tooltips
171
- are used in some of the existing implementations.
172
-
173
- You also need a javascript section at the top
174
- with a function called +mooky_init()+. This will be called when the page is loaded. If you don't
175
- have plans to do anything fancy here, just leave it blank.
176
-
177
- === Wiring it all up
178
-
179
- Just edit +lib/rscm.rb+ and +require+ your new scm class. Now you should see it coming up
180
- on the Source Control tab in the web interface.
163
+ == Web interface (DamageControl only)
181
164
 
165
+ DamageControl automatically detects new SCM classes in RSCM and generates a default web interface.
166
+
182
167
  = Building RSCM
183
168
  This section is for developers who are new to ruby development and do not already know how to build and install Ruby gems.
184
169
 
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ require 'lib/rubyforge_file_publisher'
11
11
 
12
12
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
13
13
  PKG_NAME = 'rscm'
14
- PKG_VERSION = '0.1.0' + PKG_BUILD
14
+ PKG_VERSION = '0.2.0' + PKG_BUILD
15
15
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
16
16
 
17
17
  desc "Default Task"
@@ -108,28 +108,29 @@ else
108
108
  end
109
109
  end
110
110
 
111
- task :publish do
112
- # publisher = Rake::CompositePublisher.new
113
- # publisher.add Rake::RubyForgePublisher.new('rscm', 'aslak_hellesoy')
114
-
115
- RUBYFORGE_GROUP_ID = 490
116
- RUBYFORGE_PACKAGE_ID = 552
117
- RUBYFORGE_RELEASE_NAME = "rakedrelease"
118
-
119
- Rake::RubyForgeFilePublisher.new(
120
- RUBYFORGE_GROUP_ID,
121
- "aslak_hellesoy",
122
- "README",
123
- #"pkg/#{PKG_FILE_NAME}.gem",
124
- RUBYFORGE_PACKAGE_ID,
125
- RUBYFORGE_RELEASE_NAME
126
- ) do |p|
127
- p.type_id = 8100
128
- p.processor_id = 2000
129
- p.preformatted = 1
130
- p.release_name = "come on"
131
- p.release_changes = "now"
132
- p.release_date = Time.utc(2005, 2, 19, 23, 42, 0)
133
- end
111
+ task :publish => [:rdoc] do
112
+ publisher = Rake::CompositePublisher.new
113
+ publisher.add Rake::RubyForgePublisher.new('rscm', 'aslak_hellesoy')
114
+ publisher.upload
115
+
116
+ # RUBYFORGE_GROUP_ID = 490
117
+ # RUBYFORGE_PACKAGE_ID = 552
118
+ # RUBYFORGE_RELEASE_NAME = "rakedrelease"
119
+ #
120
+ # Rake::RubyForgeFilePublisher.new(
121
+ # RUBYFORGE_GROUP_ID,
122
+ # "aslak_hellesoy",
123
+ # "README",
124
+ # #"pkg/#{PKG_FILE_NAME}.gem",
125
+ # RUBYFORGE_PACKAGE_ID,
126
+ # RUBYFORGE_RELEASE_NAME
127
+ # ) do |p|
128
+ # p.type_id = 8100
129
+ # p.processor_id = 2000
130
+ # p.preformatted = 1
131
+ # p.release_name = "come on"
132
+ # p.release_changes = "now"
133
+ # p.release_date = Time.utc(2005, 2, 19, 23, 42, 0)
134
+ # end
134
135
 
135
136
  end
@@ -2,9 +2,4 @@ require 'rscm/abstract_scm'
2
2
  require 'rscm/changes'
3
3
  require 'rscm/logging'
4
4
  require 'rscm/time_ext'
5
- # scms
6
- require 'rscm/mooky/mooky' # for demo purposes only
7
- require 'rscm/cvs/cvs'
8
- require 'rscm/darcs/darcs'
9
- require 'rscm/starteam/starteam'
10
- require 'rscm/svn/svn'
5
+ require 'rscm/abstract_scm'
@@ -1,6 +1,7 @@
1
1
  require 'fileutils'
2
2
  require 'rscm/changes'
3
3
  require 'rscm/path_converter'
4
+ require 'rscm/annotations'
4
5
 
5
6
  class String
6
7
  # Turns a String into a Time or an int
@@ -40,6 +41,20 @@ module RSCM
40
41
  class AbstractSCM
41
42
  include FileUtils
42
43
 
44
+ @@classes = []
45
+ def self.register(cls)
46
+ @@classes << cls unless @@classes.index(cls)
47
+ end
48
+ def self.classes
49
+ @@classes
50
+ end
51
+
52
+ # Load all sources under scm, so SCM classes can register themselves
53
+ Dir[File.dirname(__FILE__) + "/scm/*.rb"].each do |src|
54
+ load(src)
55
+ end
56
+
57
+
43
58
  # TODO: Make changesets yield changesets as they are determined, to avoid
44
59
  # having to load them all into memory before the method exits. Careful not to
45
60
  # use yielded changesets to do another scm hit - like get diffs. Some SCMs
@@ -62,8 +77,13 @@ module RSCM
62
77
  false
63
78
  end
64
79
 
65
- # Creates a new repository. Throws an exception if the
66
- # repository cannot be created.
80
+ # Creates a new 'central' repository. This is intended only for creation of 'central'
81
+ # repositories (not for working copies). You shouldn't have to call this method if a central repository
82
+ # already exists. This method is used primarily for testing of RSCM, but can also
83
+ # be used if you *really* want to create a central repository.
84
+ #
85
+ # This method should throw an exception if the repository cannot be created (for
86
+ # example if the repository is 'remote' or if it already exists).
67
87
  #
68
88
  def create
69
89
  end
@@ -71,6 +91,7 @@ module RSCM
71
91
  # Whether a repository can be created.
72
92
  #
73
93
  def can_create?
94
+ false
74
95
  end
75
96
 
76
97
  # Recursively imports files from a directory
@@ -96,6 +117,8 @@ module RSCM
96
117
  end
97
118
 
98
119
  # Checks out or updates contents from an SCM to +checkout_dir+ - a local working copy.
120
+ # If this is a distributed SCM, this method should create a 'working copy' repository
121
+ # if one doesn't already exist.
99
122
  #
100
123
  # The +to_identifier+ parameter may be optionally specified to obtain files up to a
101
124
  # particular time or label. +time_label+ should either be a Time (in UTC - according to
@@ -108,13 +131,16 @@ module RSCM
108
131
  # For some SCMs this is not possible, or at least very hard. In that case, just override
109
132
  # the checkout_silent method instead of this method (should be protected).
110
133
  def checkout(checkout_dir, to_identifier=Time.infinity) # :yield: file
111
- before = Dir["#{checkout_dir}/**/*"]
134
+ checkout_time = Time.now
112
135
 
113
136
  # We expect subclasses to implement this as a protected method (unless this whole method is overridden).
114
137
  checkout_silent(checkout_dir, to_identifier)
115
138
 
116
- after = Dir["#{checkout_dir}/**/*"]
117
- added = (after - before)
139
+ files = Dir["#{checkout_dir}/**/*"]
140
+ added = []
141
+ files.each do |file|
142
+ added << file if File.mtime(file) > checkout_time
143
+ end
118
144
  ignore_paths.each do |regex|
119
145
  added.delete_if{|path| path =~ regex}
120
146
  end
@@ -123,7 +149,7 @@ module RSCM
123
149
  end
124
150
  relative_added_file_paths = to_relative(checkout_dir, added_file_paths)
125
151
  relative_added_file_paths.each do |path|
126
- yield path
152
+ yield path if block_given?
127
153
  end
128
154
  relative_added_file_paths
129
155
  end
@@ -131,7 +157,7 @@ module RSCM
131
157
  # Returns a ChangeSets object for the period specified by +from_identifier+
132
158
  # and +to_identifier+. See AbstractSCM for details about the parameters.
133
159
  #
134
- def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity, files=nil)
160
+ def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
135
161
  # Should be overridden by subclasses
136
162
  changesets = ChangeSets.new
137
163
  changesets.add(
@@ -149,8 +175,8 @@ module RSCM
149
175
  changesets
150
176
  end
151
177
 
152
- # Whether the working copy in +checkout_dir+ is uptodate with the repository
153
- # since +from_identifier+.
178
+ # Whether the working copy in +checkout_dir+ is in synch with the central
179
+ # repository since +from_identifier+.
154
180
  #
155
181
  def uptodate?(checkout_dir, from_identifier)
156
182
  # Suboptimal algorithm that works for all SCMs.
@@ -159,7 +185,7 @@ module RSCM
159
185
  changesets(checkout_dir, from_identifier).empty?
160
186
  end
161
187
 
162
- # Whether the project is checked out or not.
188
+ # Whether the project is checked out from the central repository or not.
163
189
  # Subclasses should override this to check for SCM-specific administrative
164
190
  # files if appliccable
165
191
  def checked_out?(checkout_dir)
@@ -0,0 +1,50 @@
1
+ class Class
2
+ @@anns = {}
3
+
4
+ # Defines annotation(s) for the next defined +attr_reader+ or
5
+ # +attr_accessor+. The +anns+ argument should be a Hash defining annotations
6
+ # for the associated attr. Example:
7
+ #
8
+ # require 'rscm/annotations'
9
+ #
10
+ # class EmailSender
11
+ # ann :description => "IP address of the mail server", :tip => "Use 'localhost' if you have a good box, sister!"
12
+ # attr_accessor :server
13
+ # end
14
+ #
15
+ # The EmailSender class' annotations can then be accessed like this:
16
+ #
17
+ # EmailSender.server[:description] # => "IP address of the mail server"
18
+ #
19
+ # Yeah right, cool, whatever. What's this good for? It's useful for example if you want to
20
+ # build some sort of user interface (for example in on Ruby on Rails) that allows editing of
21
+ # fields, and you want to provide an explanatory text and a tooltip in the UI.
22
+ #
23
+ # You may also use annotations to specify more programmatically meaningful metadata. More power to you.
24
+ #
25
+ def ann(anns)
26
+ $attr_anns ||= {}
27
+ $attr_anns.merge!(anns)
28
+ end
29
+
30
+ def method_missing(sym, *args) #:nodoc:
31
+ anns = @@anns[self]
32
+ return superclass.method_missing(sym, *args) if(anns.nil?)
33
+ anns[sym]
34
+ end
35
+
36
+ alias old_attr_reader attr_reader #:nodoc:
37
+ def attr_reader(*syms) #:nodoc:
38
+ @@anns[self] ||= {}
39
+ syms.each do |sym|
40
+ @@anns[self][sym] = $attr_anns.dup if $attr_anns
41
+ end
42
+ $attr_anns = nil
43
+ old_attr_reader(*syms)
44
+ end
45
+
46
+ def attr_accessor(*syms) #:nodoc:
47
+ attr_reader(*syms)
48
+ attr_writer(*syms)
49
+ end
50
+ end
@@ -3,10 +3,7 @@ require 'rscm/time_ext'
3
3
 
4
4
  module RSCM
5
5
 
6
- # TODO: add a hook to get committers from a separate class - to support registered pairs
7
- # We'll be able to do lots of cool analysis with visitors later -> graphs. mmmmm.
8
-
9
- # A collection of changesets.
6
+ # A collection of ChangeSet.
10
7
  class ChangeSets
11
8
  include Enumerable
12
9
  include XMLRPC::Marshallable
@@ -220,7 +217,7 @@ module RSCM
220
217
  # This is a UTC ruby time
221
218
  attr_accessor :time
222
219
 
223
- def initialize(path=nil, developer=nil, message=nil, revision=nil, time=nil, status=DELETED)
220
+ def initialize(path=nil, status=nil, developer=nil, message=nil, revision=nil, time=nil)
224
221
  @path, @developer, @message, @revision, @time, @status = path, developer, message, revision, time, status
225
222
  end
226
223
 
@@ -2,4 +2,5 @@ require 'rubygems'
2
2
  require_gem 'log4r'
3
3
 
4
4
  Log = Log4r::Logger.new("rscm")
5
+ Log.level = ENV["LOG4R_LEVEL"] ? ENV["LOG4R_LEVEL"].to_i : 0
5
6
  Log.add Log4r::Outputter.stderr
@@ -21,10 +21,11 @@ def with_working_dir(dir)
21
21
  #
22
22
  prev = Dir.pwd
23
23
  begin
24
- Log.info "Making directory: '#{File.expand_path(dir)}'"
24
+ dir = File.expand_path(dir)
25
+ Log.info "Making directory: '#{dir}'"
25
26
  FileUtils.mkdir_p(dir)
26
27
  Dir.chdir(dir)
27
- Log.info "In directory: '#{File.expand_path(dir)}'"
28
+ Log.info "In directory: '#{dir}'"
28
29
  yield
29
30
  ensure
30
31
  Dir.chdir(prev)
@@ -1,7 +1,7 @@
1
1
  require 'rscm/abstract_scm'
2
2
  require 'rscm/path_converter'
3
3
  require 'rscm/line_editor'
4
- require 'rscm/cvs/cvs_log_parser'
4
+ require 'rscm/scm/cvs_log_parser'
5
5
 
6
6
  module RSCM
7
7
 
@@ -10,12 +10,19 @@ module RSCM
10
10
  # You need a cvs executable on the PATH in order for it to work.
11
11
  #
12
12
  # NOTE: On Cygwin this has to be the win32 build of cvs and not the Cygwin one.
13
- class CVS < AbstractSCM
13
+ class Cvs < AbstractSCM
14
+ register self
14
15
 
15
- public
16
+ ann :description => "CVSROOT"
16
17
  attr_accessor :root
18
+
19
+ ann :description => "module"
17
20
  attr_accessor :mod
21
+
22
+ ann :description => "Branch"
18
23
  attr_accessor :branch
24
+
25
+ ann :description => "Password", :tip => "<b>Warning!</b> Password will be shown in cleartext in configuration files."
19
26
  attr_accessor :password
20
27
 
21
28
  def initialize(root=nil, mod=nil, branch=nil, password=nil)
@@ -89,9 +96,9 @@ module RSCM
89
96
  files.empty?
90
97
  end
91
98
 
92
- def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity, files=nil)
99
+ def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
93
100
  checkout(checkout_dir) unless uptodate?(checkout_dir, nil) # must checkout to get changesets
94
- parse_log(checkout_dir, changes_command(from_identifier, to_identifier, files))
101
+ parse_log(checkout_dir, changes_command(from_identifier, to_identifier))
95
102
  end
96
103
 
97
104
  def diff(checkout_dir, change)
@@ -226,7 +233,7 @@ module RSCM
226
233
  changesets = nil
227
234
  with_working_dir(checkout_dir) do
228
235
  safer_popen(execed_command_line) do |stdout|
229
- parser = CVSLogParser.new(stdout)
236
+ parser = CvsLogParser.new(stdout)
230
237
  parser.cvspath = path
231
238
  parser.cvsmodule = mod
232
239
  changesets = parser.parse_changesets
@@ -235,7 +242,7 @@ module RSCM
235
242
  changesets
236
243
  end
237
244
 
238
- def changes_command(from_identifier, to_identifier, files)
245
+ def changes_command(from_identifier, to_identifier)
239
246
  # https://www.cvshome.org/docs/manual/cvs-1.11.17/cvs_16.html#SEC144
240
247
  # -N => Suppress the header if no revisions are selected.
241
248
  "log #{branch_option} -N #{period_option(from_identifier, to_identifier)}"
@@ -293,11 +300,11 @@ module RSCM
293
300
 
294
301
  def command_line(cmd, password=nil, simulate=false)
295
302
  cvs_options = simulate ? "-n" : ""
296
- "cvs \"-d#{root_with_password(password)}\" #{cvs_options} -q #{cmd}"
303
+ "cvs -f \"-d#{root_with_password(password)}\" #{cvs_options} -q #{cmd}"
297
304
  end
298
305
 
299
306
  def create_root_cvs
300
- CVS.new(self.root, "CVSROOT", nil, self.password)
307
+ Cvs.new(self.root, "CVSROOT", nil, self.password)
301
308
  end
302
309
 
303
310
  def revision_option(identifier)
@@ -5,9 +5,9 @@ require 'ftools'
5
5
 
6
6
  module RSCM
7
7
 
8
- class CVSLogParser < AbstractLogParser
9
- REVISION_SEPARATOR = /^----------------------------$/
10
- ENTRY_SEPARATOR = /^=============================================================================$/
8
+ class CvsLogParser < AbstractLogParser
9
+ REVISION_SEPARATOR = /^----------------------------$/ unless defined? REVISION_SEPARATOR
10
+ ENTRY_SEPARATOR = /^=============================================================================$/ unless defined? ENTRY_SEPARATOR
11
11
 
12
12
  attr_accessor :cvspath
13
13
  attr_accessor :cvsmodule
@@ -154,7 +154,7 @@ module RSCM
154
154
  # The state field is "Exp" both for added and modified files. retards!
155
155
  # We need some additional logic to figure out whether it is added or not.
156
156
  # Maybe look at the revision. (1.1 means new I think. - deal with it later)
157
- STATES = {"dead" => Change::DELETED, "Exp" => Change::MODIFIED}
157
+ STATES = {"dead" => Change::DELETED, "Exp" => Change::MODIFIED} unless defined? STATES
158
158
 
159
159
  end
160
160