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