rscm 0.2.1.1404 → 0.3.0
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/README +34 -23
- data/Rakefile +24 -29
- data/bin/touch.exe +0 -0
- data/lib/rscm.rb +6 -3
- data/lib/rscm/annotations.rb +26 -7
- data/lib/rscm/{abstract_scm.rb → base.rb} +109 -71
- data/lib/rscm/better.rb +16 -0
- data/lib/rscm/logging.rb +11 -5
- data/lib/rscm/path_converter.rb +9 -16
- data/lib/rscm/revision.rb +201 -0
- data/lib/rscm/revision_file.rb +71 -0
- data/lib/rscm/scm/clearcase.rb +7 -7
- data/lib/rscm/scm/cvs.rb +69 -70
- data/lib/rscm/scm/cvs_log_parser.rb +29 -29
- data/lib/rscm/scm/darcs.rb +82 -34
- data/lib/rscm/scm/darcs_log_parser.rb +65 -0
- data/lib/rscm/scm/monotone.rb +249 -77
- data/lib/rscm/scm/monotone_log_parser.rb +57 -43
- data/lib/rscm/scm/mooky.rb +3 -3
- data/lib/rscm/scm/perforce.rb +196 -134
- data/lib/rscm/scm/star_team.rb +10 -10
- data/lib/rscm/scm/subversion.rb +106 -77
- data/lib/rscm/scm/subversion_log_parser.rb +76 -47
- data/lib/rscm/time_ext.rb +2 -116
- data/test/rscm/annotations_test.rb +15 -2
- data/test/rscm/{abstract_scm_test.rb → base_test.rb} +3 -3
- data/test/rscm/difftool_test.rb +9 -3
- data/test/rscm/generic_scm_tests.rb +195 -124
- data/test/rscm/revision_fixture.rb +20 -0
- data/test/rscm/revision_test.rb +129 -0
- data/test/rscm/{changesets.yaml → revisions.yaml} +10 -10
- data/test/rscm/scm/clearcase.log +608 -0
- data/test/rscm/scm/clearcase_test.rb +39 -0
- data/test/rscm/scm/cvs_log_parser_test.rb +73 -73
- data/test/rscm/scm/cvs_test.rb +1 -1
- data/test/rscm/scm/darcs_log_parser_test.rb +171 -0
- data/test/rscm/scm/monotone_log_parser_test.rb +49 -31
- data/test/rscm/scm/monotone_test.rb +3 -2
- data/test/rscm/scm/p4client_test.rb +33 -0
- data/test/rscm/scm/perforce_test.rb +25 -3
- data/test/rscm/scm/star_team.rb +9 -9
- data/test/rscm/scm/subversion_log_parser_test.rb +107 -47
- metadata +17 -13
- data/lib/multipart.rb +0 -95
- data/lib/rscm/RSS.txt +0 -41
- data/lib/rscm/changes.rb +0 -268
- data/lib/rscm/example.yaml +0 -21
- data/lib/rubyforge_file_publisher.rb +0 -176
- data/test/rscm/changes_fixture.rb +0 -20
- data/test/rscm/changes_test.rb +0 -129
data/README
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
= RSCM - Ruby Source Control Management (0.
|
1
|
+
= RSCM - Ruby Source Control Management (0.3.0)
|
2
2
|
|
3
|
-
RSCM is to SCM what DBI/JDBC/ODBC are to databases - an SCM-independent API for accessing
|
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
|
|
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)
|
@@ -8,15 +8,25 @@ RSCM is to SCM what DBI/JDBC/ODBC are to databases - an SCM-independent API for
|
|
8
8
|
* Add and commit files
|
9
9
|
* Manipluate triggers
|
10
10
|
|
11
|
+
Although RSCM's main focus is operations on a working copy of an SCM repository, the API also allows some level of interaction
|
12
|
+
with the SCM repository itself, like creating new repositories.
|
13
|
+
|
11
14
|
== Download
|
12
15
|
|
13
16
|
RSCM is available as a RubyGem, and can be installed like this:
|
14
17
|
|
15
18
|
gem install rscm
|
16
19
|
|
20
|
+
(You may need administer access to do this on a POSIX system).
|
17
21
|
If you want the latest and greatest, you can get the sources, which live alongside DamageControl's sources:
|
18
22
|
|
19
|
-
|
23
|
+
* See the DamageControl Developer Guide at http://hieraki.lavalamp.ca/
|
24
|
+
|
25
|
+
== Team
|
26
|
+
* Aslak Hellesoy - All
|
27
|
+
* Steven Baker - Monotone
|
28
|
+
* Jon Tirsen - CVS, Subversion
|
29
|
+
* Yogi Kulkarni - Perforce
|
20
30
|
|
21
31
|
== Supported SCMs
|
22
32
|
|
@@ -30,7 +40,7 @@ In progress:
|
|
30
40
|
* ClearCase -http://www-306.ibm.com/software/awdtools/clearcase (half way there)
|
31
41
|
* Darcs - http://www.abridgegame.org/darcs (very incomplete)
|
32
42
|
* Monotone - http://www.venge.net/monotone (half way there)
|
33
|
-
|
43
|
+
|
34
44
|
Planned:
|
35
45
|
|
36
46
|
Loads! All of them! How to add support for a new one is described further down in this file.
|
@@ -46,29 +56,30 @@ Here is an example of how to use RSCM to get a list of changesets from a CVS rep
|
|
46
56
|
require 'rscm'
|
47
57
|
|
48
58
|
scm = RSCM::Subversion.new("svn://some.server/some/path/trunk", "trunk")
|
59
|
+
scm.checkout_dir = "/my/working/copy"
|
49
60
|
|
50
|
-
scm.checkout
|
51
|
-
|
52
|
-
|
53
|
-
puts
|
61
|
+
scm.checkout
|
62
|
+
revisions = scm.revisions(Time.utc(2004, 11, 10, 12, 34, 22))
|
63
|
+
revisions.each do |revision|
|
64
|
+
puts revision # or do something more funky with it
|
54
65
|
end
|
55
66
|
|
56
67
|
=== Using visitors
|
57
68
|
|
58
|
-
Although
|
59
|
-
|
69
|
+
Although the Revisions and Revision classes support external iteration
|
70
|
+
(with +each+ as in the example above), they also support
|
71
|
+
visitor traversal via their +accept+ methods. A visitor
|
60
72
|
must respond to the following methods:
|
61
73
|
|
62
|
-
def
|
63
|
-
def
|
64
|
-
def
|
74
|
+
def visit_revisions(revisions); end
|
75
|
+
def visit_revision(revision); end
|
76
|
+
def visit_file(revision_file); end
|
65
77
|
|
66
78
|
=== Getting diffs
|
67
79
|
|
68
|
-
RSCM allows you to get diffs for changesets.
|
69
|
-
|
70
|
-
|
71
|
-
and presents them as colourised diffs in its user interface).
|
80
|
+
RSCM allows you to get diffs for changesets. See the DamageControl sources for more detailed
|
81
|
+
examples (DamageControl persists diffs to disk and presents them as colourised diffs in its
|
82
|
+
user interface).
|
72
83
|
|
73
84
|
== Future plans
|
74
85
|
|
@@ -84,7 +95,7 @@ the central SCM and one that you set up on your local machine.
|
|
84
95
|
= Implementing support for a new SCM
|
85
96
|
|
86
97
|
We'd be happy to receive contributions for more SCMs. You can always
|
87
|
-
file a JIRA issue and hope for someone to implement it for you, or
|
98
|
+
file a JIRA issue (http://jira.codehaus.org/browse/DC) and hope for someone to implement it for you, or
|
88
99
|
you can do it yourself. The rest of this file should get you started.
|
89
100
|
|
90
101
|
N.B. IF YOU START IMPLEMENTING A NEW RSCM PLUGIN, PLEASE SUBMIT YOUR CODE
|
@@ -105,7 +116,7 @@ acceptance test suite for RSCM. By doing this you'll actually follow a TDD
|
|
105
116
|
approach for your new Mooky class - except that the tests are already written
|
106
117
|
for you!
|
107
118
|
|
108
|
-
IMPORTANT NOTE: If your SCM doesn't provide an easy way to create new local repositories
|
119
|
+
IMPORTANT NOTE: If your SCM doesn't provide an easy way to create new local repositories
|
109
120
|
(such as with StarTeam) you're probably better off writing the tests from scratch and not
|
110
121
|
include GenericSCMTests. Instead, just make sure you have an SCM repository set up somewhere
|
111
122
|
and write tests to work against that repository. This way you won't be able to pass the
|
@@ -162,8 +173,8 @@ You can reuse these classes for other Java based SCMs (if there are any, I don't
|
|
162
173
|
|
163
174
|
== Web interface (DamageControl only)
|
164
175
|
|
165
|
-
DamageControl automatically detects new SCM classes in RSCM and generates a default web interface.
|
166
|
-
|
176
|
+
DamageControl automatically detects new SCM classes in RSCM and generates a default web interface.
|
177
|
+
|
167
178
|
= Building RSCM
|
168
179
|
This section is for developers who are new to ruby development and do not already know how to build and install Ruby gems.
|
169
180
|
|
@@ -178,6 +189,6 @@ Now change to the RSCM root directory and type
|
|
178
189
|
|
179
190
|
This will create a gem for RSCM. To install this gem, you have to change to the pgk directory and type
|
180
191
|
|
181
|
-
sudo gem install pkg/rscm-0.
|
192
|
+
sudo gem install pkg/rscm-0.3.0.gem
|
182
193
|
|
183
|
-
Now you can use RSCM in other Ruby apps.
|
194
|
+
Now you can use RSCM in other Ruby apps with a simple require 'rscm'.
|
data/Rakefile
CHANGED
@@ -4,14 +4,13 @@ require 'rake/testtask'
|
|
4
4
|
require 'rake/rdoctask'
|
5
5
|
require 'rake/packagetask'
|
6
6
|
require 'rake/gempackagetask'
|
7
|
-
require 'rake/contrib/compositepublisher'
|
8
7
|
require 'rake/contrib/sshpublisher'
|
9
8
|
require 'rake/contrib/rubyforgepublisher'
|
10
|
-
require '
|
9
|
+
require 'xforge'
|
11
10
|
|
12
11
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
13
12
|
PKG_NAME = 'rscm'
|
14
|
-
PKG_VERSION = '0.
|
13
|
+
PKG_VERSION = '0.3.0' + PKG_BUILD
|
15
14
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
16
15
|
|
17
16
|
desc "Default Task"
|
@@ -22,9 +21,11 @@ task :all => [:ant, :gem]
|
|
22
21
|
# To run a specific test: rake test TEST=path/to/test
|
23
22
|
fl = FileList.new('test/**/*_test.rb')
|
24
23
|
fl.exclude('test/**/mooky*.rb')
|
25
|
-
fl.exclude('test/**/monotone*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
24
|
+
#fl.exclude('test/**/monotone*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
25
|
+
fl.exclude('test/**/clearcase*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
26
26
|
fl.exclude('test/**/darcs*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
27
|
-
fl.exclude('test/**/perforce*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
27
|
+
#fl.exclude('test/**/perforce*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
28
|
+
#fl.exclude('test/**/p4client*.rb') # Incomplete/unsupported for now - reactivate when more complete!
|
28
29
|
fl.exclude('test/**/starteam*.rb') # Too bloody hard to test without a StarTeam server license! Tested ad-hoc.
|
29
30
|
Rake::TestTask.new { |t|
|
30
31
|
t.libs << "test"
|
@@ -109,29 +110,23 @@ else
|
|
109
110
|
end
|
110
111
|
end
|
111
112
|
|
112
|
-
task :
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
# ) do |p|
|
129
|
-
# p.type_id = 8100
|
130
|
-
# p.processor_id = 2000
|
131
|
-
# p.preformatted = 1
|
132
|
-
# p.release_name = "come on"
|
133
|
-
# p.release_changes = "now"
|
134
|
-
# p.release_date = Time.utc(2005, 2, 19, 23, 42, 0)
|
135
|
-
# end
|
113
|
+
task :release => [:publish_doc, :publish_files]
|
114
|
+
|
115
|
+
task :publish_files => [:gem] do
|
116
|
+
release_files = FileList[
|
117
|
+
"pkg/#{PKG_FILE_NAME}.gem",
|
118
|
+
"CHANGES"
|
119
|
+
]
|
120
|
+
|
121
|
+
Rake::XForge::Release.new(PKG_NAME) do |xf|
|
122
|
+
# Never hardcode user name and password in the Rakefile!
|
123
|
+
xf.user_name = ENV['RUBYFORGE_USER']
|
124
|
+
xf.password = ENV['RUBYFORGE_PASSWORD']
|
125
|
+
xf.files = release_files.to_a
|
126
|
+
xf.release_name = "XForge #{PKG_VERSION}"
|
127
|
+
end
|
128
|
+
end
|
136
129
|
|
130
|
+
task :publish_doc => [:rdoc] do
|
131
|
+
publisher = Rake::RubyForgePublisher.new('rscm', 'aslak_hellesoy')
|
137
132
|
end
|
data/bin/touch.exe
ADDED
Binary file
|
data/lib/rscm.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
require 'rscm/
|
2
|
-
require 'rscm/
|
1
|
+
require 'rscm/annotations'
|
2
|
+
require 'rscm/path_converter'
|
3
3
|
require 'rscm/logging'
|
4
|
+
require 'rscm/better'
|
5
|
+
require 'rscm/base'
|
6
|
+
require 'rscm/revision'
|
7
|
+
require 'rscm/revision_file'
|
4
8
|
require 'rscm/time_ext'
|
5
|
-
require 'rscm/abstract_scm'
|
data/lib/rscm/annotations.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
class Class
|
2
|
+
|
2
3
|
@@anns = {}
|
4
|
+
@@attr_anns = {}
|
3
5
|
|
4
6
|
# Defines annotation(s) for the next defined +attr_reader+ or
|
5
7
|
# +attr_accessor+. The +anns+ argument should be a Hash defining annotations
|
@@ -23,23 +25,24 @@ class Class
|
|
23
25
|
# You may also use annotations to specify more programmatically meaningful metadata. More power to you.
|
24
26
|
#
|
25
27
|
def ann(anns)
|
26
|
-
|
27
|
-
|
28
|
+
@@attr_anns ||= {}
|
29
|
+
@@attr_anns.merge!(anns)
|
28
30
|
end
|
29
|
-
|
31
|
+
|
30
32
|
def method_missing(sym, *args) #:nodoc:
|
31
33
|
anns = @@anns[self]
|
32
|
-
return
|
33
|
-
|
34
|
+
return anns[sym] if(anns && anns[sym])
|
35
|
+
return superclass.method_missing(sym, *args) if superclass
|
36
|
+
return {}
|
34
37
|
end
|
35
38
|
|
36
39
|
alias old_attr_reader attr_reader #:nodoc:
|
37
40
|
def attr_reader(*syms) #:nodoc:
|
38
41
|
@@anns[self] ||= {}
|
39
42
|
syms.each do |sym|
|
40
|
-
@@anns[self][sym] =
|
43
|
+
@@anns[self][sym] = @@attr_anns.dup if @@attr_anns
|
41
44
|
end
|
42
|
-
|
45
|
+
@@attr_anns = nil
|
43
46
|
old_attr_reader(*syms)
|
44
47
|
end
|
45
48
|
|
@@ -48,3 +51,19 @@ class Class
|
|
48
51
|
attr_writer(*syms)
|
49
52
|
end
|
50
53
|
end
|
54
|
+
|
55
|
+
class Object
|
56
|
+
def anns(attr_name)
|
57
|
+
self.class.send(attr_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def __attr_accessors
|
61
|
+
attrs = []
|
62
|
+
methods.each do |method|
|
63
|
+
if(method =~ /(.*)=/)
|
64
|
+
attrs << "@#{$1}" if methods.index($1) && $1 != "=="
|
65
|
+
end
|
66
|
+
end
|
67
|
+
attrs.sort
|
68
|
+
end
|
69
|
+
end
|
@@ -1,44 +1,57 @@
|
|
1
1
|
require 'fileutils'
|
2
|
-
require 'rscm/
|
2
|
+
require 'rscm/revision'
|
3
3
|
require 'rscm/path_converter'
|
4
4
|
require 'rscm/annotations'
|
5
5
|
|
6
|
-
class String
|
7
|
-
# Turns a String into a Time or an int
|
8
|
-
def to_identifier
|
9
|
-
if(self =~ /20\d\d\d\d\d\d\d\d\d\d\d\d/)
|
10
|
-
# Assume it's a timestamp string - convert to time.
|
11
|
-
Time.parse_ymdHMS(self)
|
12
|
-
else
|
13
|
-
# Assume it's an arbitrary integer.
|
14
|
-
self.to_i
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class Time
|
20
|
-
def to_s
|
21
|
-
self.ymdHMS
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
6
|
module RSCM
|
26
7
|
# This class defines the RSCM API. The documentation of the various methods
|
27
|
-
# uses CVS' terminology.
|
8
|
+
# uses CVS and Subversion's terminology. (For example, checkout means 'get working copy',
|
9
|
+
# not 'lock for private edit' as in ClearCase or VSS terminology).
|
10
|
+
#
|
11
|
+
# Concrete subclasses of this class provide an API to manage a local working copy
|
12
|
+
# as well as an associated 'central' repository. The main responsibility is working
|
13
|
+
# copy operations:
|
14
|
+
#
|
15
|
+
# * checkout
|
16
|
+
# * revisions
|
17
|
+
# * uptodate?
|
18
|
+
# * checked_out?
|
19
|
+
# * diff
|
20
|
+
# * edit
|
21
|
+
#
|
22
|
+
# In addition to operations related to working copies, the same instance should provide
|
23
|
+
# methods to administer the working copy's associated 'central' repository. These are:
|
24
|
+
#
|
25
|
+
# * central_exists?
|
26
|
+
# * create_central
|
27
|
+
# * can_create_central?
|
28
|
+
# * import_central
|
29
|
+
# * supports_trigger? # TODO: rename to can_install_trigger?
|
30
|
+
# * trigger_installed?
|
31
|
+
# * install_trigger
|
32
|
+
# * uninstall_trigger
|
33
|
+
#
|
34
|
+
# Some methods are a bit fuzzy with respect to their relevance to the working copy or
|
35
|
+
# the associated central repository, as it depends on the nature of the individual underlying
|
36
|
+
# SCMs. These methods are:
|
37
|
+
#
|
38
|
+
# * label
|
39
|
+
# * name
|
40
|
+
# * transactional?
|
41
|
+
# * checkout_command_line
|
42
|
+
# * update_command_line
|
28
43
|
#
|
29
44
|
# Some of the methods in this API use +from_identifier+ and +to_identifier+.
|
30
45
|
# These identifiers can be either a UTC Time (according to the SCM's clock)
|
31
|
-
# or a String representing a label
|
46
|
+
# or a String or Integer representing a label/revision
|
47
|
+
# (according to the SCM's native label/revision scheme).
|
32
48
|
#
|
33
|
-
# If +from_identifier+ or +to_identifier+ are +nil+ they respectively
|
34
|
-
# epoch or
|
49
|
+
# If +from_identifier+ or +to_identifier+ are +nil+ they should respectively default to
|
50
|
+
# Time.epoch or Time.infinite.
|
35
51
|
#
|
36
|
-
#
|
37
|
-
# unnecessary. The reason is that some SCMs require a working copy to be checked
|
38
|
-
# out in order to perform certain operations. In order to develop portable code,
|
39
|
-
# you should always pass in a non +nil+ value for +checkout_dir+.
|
52
|
+
# TODO: rename this superclass to 'Base'
|
40
53
|
#
|
41
|
-
class
|
54
|
+
class Base
|
42
55
|
include FileUtils
|
43
56
|
|
44
57
|
@@classes = []
|
@@ -51,27 +64,35 @@ module RSCM
|
|
51
64
|
|
52
65
|
# Load all sources under scm, so SCM classes can register themselves
|
53
66
|
Dir[File.dirname(__FILE__) + "/scm/*.rb"].each do |src|
|
54
|
-
|
67
|
+
require src
|
55
68
|
end
|
56
69
|
|
57
70
|
|
58
|
-
# TODO: Make
|
71
|
+
# TODO: Make revisions yield revisions as they are determined, to avoid
|
59
72
|
# having to load them all into memory before the method exits. Careful not to
|
60
|
-
# use yielded
|
73
|
+
# use yielded revisions to do another scm hit - like get diffs. Some SCMs
|
61
74
|
# might dead lock on this. Implement a guard for that.
|
62
75
|
# TODO: Add some visitor support here too?
|
63
76
|
|
64
77
|
public
|
65
78
|
|
79
|
+
attr_accessor :checkout_dir
|
80
|
+
|
81
|
+
def to_yaml_properties
|
82
|
+
props = instance_variables
|
83
|
+
props.delete("@checkout_dir")
|
84
|
+
props.sort!
|
85
|
+
end
|
86
|
+
|
66
87
|
# Whether the physical SCM represented by this instance exists.
|
67
88
|
#
|
68
|
-
def
|
89
|
+
def central_exists?
|
69
90
|
# The default implementation assumes yes - override if it can be
|
70
91
|
# determined programmatically.
|
71
92
|
true
|
72
93
|
end
|
73
94
|
|
74
|
-
# Whether or not this SCM is transactional.
|
95
|
+
# Whether or not this SCM is transactional (atomic).
|
75
96
|
#
|
76
97
|
def transactional?
|
77
98
|
false
|
@@ -85,18 +106,29 @@ module RSCM
|
|
85
106
|
# This method should throw an exception if the repository cannot be created (for
|
86
107
|
# example if the repository is 'remote' or if it already exists).
|
87
108
|
#
|
88
|
-
def
|
109
|
+
def create_central
|
110
|
+
raise "Not implemented"
|
111
|
+
end
|
112
|
+
|
113
|
+
# Destroysthe central repository. Shuts down any server processes and deletes the repository.
|
114
|
+
# WARNING: calling this may result in loss of data. Only call this if you really want to wipe it out for good!
|
115
|
+
def destroy_central
|
89
116
|
end
|
90
117
|
|
91
118
|
# Whether a repository can be created.
|
92
119
|
#
|
93
|
-
def
|
120
|
+
def can_create_central?
|
94
121
|
false
|
95
122
|
end
|
96
123
|
|
97
|
-
#
|
124
|
+
# Adds +relative_filename+ to the working copy.
|
125
|
+
def add(relative_filename)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Recursively imports files from a directory into the central scm
|
98
129
|
#
|
99
|
-
def
|
130
|
+
def import_central(dir, message)
|
131
|
+
raise "Not implemented"
|
100
132
|
end
|
101
133
|
|
102
134
|
# The display name of this SCM
|
@@ -106,23 +138,19 @@ module RSCM
|
|
106
138
|
self.class.name
|
107
139
|
end
|
108
140
|
|
109
|
-
# Gets the label for the working copy currently checked out in +checkout_dir+.
|
110
|
-
#
|
111
|
-
def label(checkout_dir)
|
112
|
-
end
|
113
|
-
|
114
141
|
# Open a file for edit - required by scms that checkout files in read-only mode e.g. perforce
|
115
142
|
#
|
116
143
|
def edit(file)
|
117
144
|
end
|
118
145
|
|
119
|
-
# Checks out or updates contents from
|
146
|
+
# Checks out or updates contents from a central SCM to +checkout_dir+ - a local working copy.
|
120
147
|
# If this is a distributed SCM, this method should create a 'working copy' repository
|
121
|
-
# if one doesn't already exist.
|
148
|
+
# if one doesn't already exist. Then the contents of the central SCM should be pulled into
|
149
|
+
# the working copy.
|
122
150
|
#
|
123
151
|
# The +to_identifier+ parameter may be optionally specified to obtain files up to a
|
124
|
-
# particular time or label. +
|
125
|
-
# the clock on the SCM machine) or a String - reprsenting a label.
|
152
|
+
# particular time or label. +to_identifier+ should either be a Time (in UTC - according to
|
153
|
+
# the clock on the SCM machine) or a String - reprsenting a label or revision.
|
126
154
|
#
|
127
155
|
# This method will yield the relative file name of each checked out file, and also return
|
128
156
|
# them in an array. Only files, not directories, should be yielded/returned.
|
@@ -130,13 +158,14 @@ module RSCM
|
|
130
158
|
# This method should be overridden for SCMs that are able to yield checkouts as they happen.
|
131
159
|
# For some SCMs this is not possible, or at least very hard. In that case, just override
|
132
160
|
# the checkout_silent method instead of this method (should be protected).
|
133
|
-
|
161
|
+
#
|
162
|
+
def checkout(to_identifier=Time.infinity) # :yield: file
|
134
163
|
# the OS doesn't store file timestamps with fractions.
|
135
164
|
before_checkout_time = Time.now.utc - 1
|
136
165
|
|
137
166
|
# We expect subclasses to implement this as a protected method (unless this whole method is overridden).
|
138
|
-
checkout_silent(
|
139
|
-
files = Dir["#{checkout_dir}/**/*"]
|
167
|
+
checkout_silent(to_identifier)
|
168
|
+
files = Dir["#{@checkout_dir}/**/*"]
|
140
169
|
added = []
|
141
170
|
files.each do |file|
|
142
171
|
added << file if File.mtime(file).utc > before_checkout_time
|
@@ -154,45 +183,49 @@ module RSCM
|
|
154
183
|
relative_added_file_paths
|
155
184
|
end
|
156
185
|
|
157
|
-
# Returns a
|
158
|
-
# and +to_identifier
|
186
|
+
# Returns a Revisions object for the period specified by +from_identifier+ (exclusive, i.e. after)
|
187
|
+
# and +to_identifier+ (inclusive).
|
159
188
|
#
|
160
|
-
def
|
189
|
+
def revisions(from_identifier, to_identifier=Time.infinity)
|
161
190
|
# Should be overridden by subclasses
|
162
|
-
|
163
|
-
|
164
|
-
|
191
|
+
revisions = Revisions.new
|
192
|
+
revisions.add(
|
193
|
+
Revision.new(
|
165
194
|
"up/the/chimney",
|
195
|
+
Revision::DELETED,
|
166
196
|
"DamageControl",
|
167
|
-
"The #{name}
|
168
|
-
"correctly implement the
|
169
|
-
"not a real
|
197
|
+
"The #{name} class doesn't\n" +
|
198
|
+
"correctly implement the revisions method. This is\n" +
|
199
|
+
"not a real revision, but a hint to the developer to go and implement it.\n\n" +
|
170
200
|
"Do It Now!",
|
171
201
|
"999",
|
172
202
|
Time.now.utc
|
173
203
|
)
|
174
204
|
)
|
175
|
-
|
205
|
+
revisions
|
176
206
|
end
|
177
207
|
|
178
|
-
# Whether the working copy
|
179
|
-
# repository
|
208
|
+
# Whether the working copy is in synch with the central
|
209
|
+
# repository's revision/time identified by +identifier+.
|
210
|
+
# If +identifier+ is nil, 'HEAD' of repository should be assumed.
|
180
211
|
#
|
181
|
-
|
212
|
+
# TODO: rename to in_synch?
|
213
|
+
def uptodate?(identifier)
|
182
214
|
# Suboptimal algorithm that works for all SCMs.
|
183
|
-
# Subclasses can override this to
|
215
|
+
# Subclasses can override this to improve efficiency.
|
184
216
|
|
185
|
-
|
217
|
+
revisions(identifier).empty?
|
186
218
|
end
|
187
219
|
|
188
220
|
# Whether the project is checked out from the central repository or not.
|
189
221
|
# Subclasses should override this to check for SCM-specific administrative
|
190
222
|
# files if appliccable
|
191
|
-
def checked_out?
|
192
|
-
File.exists?(checkout_dir)
|
223
|
+
def checked_out?
|
224
|
+
File.exists?(@checkout_dir)
|
193
225
|
end
|
194
226
|
|
195
|
-
# Whether triggers are supported by this SCM
|
227
|
+
# Whether triggers are supported by this SCM. A trigger is a command that can be executed
|
228
|
+
# upon a completed commit to the SCM.
|
196
229
|
def supports_trigger?
|
197
230
|
# The default implementation assumes no - override if it can be
|
198
231
|
# determined programmatically.
|
@@ -206,31 +239,36 @@ module RSCM
|
|
206
239
|
# Most implementations will ignore this parameter.
|
207
240
|
#
|
208
241
|
def install_trigger(trigger_command, install_dir)
|
242
|
+
raise "Not implemented"
|
209
243
|
end
|
210
244
|
|
211
245
|
# Uninstalls +trigger_command+ from the SCM.
|
212
246
|
#
|
213
247
|
def uninstall_trigger(trigger_command, install_dir)
|
248
|
+
raise "Not implemented"
|
214
249
|
end
|
215
250
|
|
216
251
|
# Whether the command denoted by +trigger_command+ is installed in the SCM.
|
217
252
|
#
|
218
253
|
def trigger_installed?(trigger_command, install_dir)
|
254
|
+
raise "Not implemented"
|
219
255
|
end
|
220
256
|
|
221
257
|
# The command line to run in order to check out a fresh working copy.
|
222
258
|
#
|
223
259
|
def checkout_commandline(to_identifier=Time.infinity)
|
260
|
+
raise "Not implemented"
|
224
261
|
end
|
225
262
|
|
226
263
|
# The command line to run in order to update a working copy.
|
227
264
|
#
|
228
265
|
def update_commandline(to_identifier=Time.infinity)
|
266
|
+
raise "Not implemented"
|
229
267
|
end
|
230
268
|
|
231
269
|
# Returns/yields an IO containing the unified diff of the change.
|
232
|
-
def diff(
|
233
|
-
return(yield("
|
270
|
+
def diff(change, &block)
|
271
|
+
return(yield("Not implemented"))
|
234
272
|
end
|
235
273
|
|
236
274
|
def ==(other_scm)
|
@@ -243,7 +281,7 @@ module RSCM
|
|
243
281
|
|
244
282
|
protected
|
245
283
|
|
246
|
-
# Takes an array of +
|
284
|
+
# Takes an array of +absolute_paths+ and turns it into an array
|
247
285
|
# of paths relative to +dir+
|
248
286
|
#
|
249
287
|
def to_relative(dir, absolute_paths)
|