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/lib/rscm/scm/star_team.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'tempfile'
|
3
|
-
require 'rscm/
|
4
|
-
require 'rscm/
|
3
|
+
require 'rscm/revision'
|
4
|
+
require 'rscm/base'
|
5
5
|
require 'yaml'
|
6
6
|
|
7
7
|
class Time
|
@@ -17,8 +17,8 @@ module RSCM
|
|
17
17
|
# * StarTeam SDK
|
18
18
|
# * Apache Ant (http://ant.apache.org/)
|
19
19
|
#
|
20
|
-
class StarTeam <
|
21
|
-
register self
|
20
|
+
class StarTeam < Base
|
21
|
+
#register self
|
22
22
|
|
23
23
|
ann :description => "User name"
|
24
24
|
attr_accessor :user_name
|
@@ -49,23 +49,23 @@ module RSCM
|
|
49
49
|
"StarTeam"
|
50
50
|
end
|
51
51
|
|
52
|
-
def
|
52
|
+
def revisions(checkout_dir, from_identifier=Time.epoch, to_identifier=Time.infinity, &proc)
|
53
53
|
# just assuming it is a Time for now, may support labels later.
|
54
54
|
# the java class really wants rfc822 and not rfc2822, but this works ok anyway.
|
55
55
|
from = from_identifier.to_rfc2822
|
56
56
|
to = to_identifier.to_rfc2822
|
57
57
|
|
58
|
-
|
59
|
-
raise "
|
58
|
+
revisions = java("getRevisions(\"#{from}\";\"#{to}\")", &proc)
|
59
|
+
raise "revisions must be of type #{Revisions.name} - was #{revisions.class.name}" unless revisions.is_a?(::RSCM::Revisions)
|
60
60
|
|
61
61
|
# Just a little sanity check
|
62
|
-
if(
|
63
|
-
latetime =
|
62
|
+
if(revisions.latest)
|
63
|
+
latetime = revisions.latest.time
|
64
64
|
if(latetime < from_identifier || to_identifier < latetime)
|
65
65
|
raise "Latest time (#{latetime}) is not within #{from_identifier}-#{to_identifier}"
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
68
|
+
revisions
|
69
69
|
end
|
70
70
|
|
71
71
|
def checkout(checkout_dir, to_identifier, &proc)
|
data/lib/rscm/scm/subversion.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rscm/
|
1
|
+
require 'rscm/base'
|
2
2
|
require 'rscm/path_converter'
|
3
3
|
require 'rscm/line_editor'
|
4
4
|
require 'rscm/scm/subversion_log_parser'
|
@@ -10,22 +10,29 @@ module RSCM
|
|
10
10
|
# You need the svn/svnadmin executable on the PATH in order for it to work.
|
11
11
|
#
|
12
12
|
# NOTE: On Cygwin these have to be the win32 builds of svn/svnadmin and not the Cygwin ones.
|
13
|
-
class Subversion <
|
13
|
+
class Subversion < Base
|
14
14
|
register self
|
15
15
|
|
16
16
|
include FileUtils
|
17
17
|
include PathConverter
|
18
18
|
|
19
19
|
ann :description => "Repository URL"
|
20
|
-
# ann :tip => "If you specify a local URL (starting with file://) DamageControl can create the repository for you after you save (unless the repository already exists).<br>Using a file:// URL will also give you the option to have DamageControl install a trigger in Subversion, so that you don't have to use polling to detect changes.<br>On Windows, file URLs must look like file:///C:/jupiter/mars"
|
21
20
|
ann :tip => "If you use ssh, specify the URL as svn+ssh://username@server/path/to/repo"
|
22
21
|
attr_accessor :url
|
23
22
|
|
23
|
+
ann :description => "Username"
|
24
|
+
ann :tip => "This only applies to svn:// URLs."
|
25
|
+
attr_accessor :username
|
26
|
+
|
27
|
+
ann :description => "Password"
|
28
|
+
ann :tip => "This only applies to svn:// URLs."
|
29
|
+
attr_accessor :password
|
30
|
+
|
24
31
|
ann :description => "Path"
|
25
|
-
ann :tip => "This
|
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."
|
26
33
|
attr_accessor :path
|
27
34
|
|
28
|
-
def initialize(url="", path="
|
35
|
+
def initialize(url="", path="")
|
29
36
|
@url, @path = url, path
|
30
37
|
end
|
31
38
|
|
@@ -33,21 +40,21 @@ module RSCM
|
|
33
40
|
"Subversion"
|
34
41
|
end
|
35
42
|
|
36
|
-
def add(
|
37
|
-
svn(checkout_dir, "add #{relative_filename}")
|
43
|
+
def add(relative_filename)
|
44
|
+
svn(@checkout_dir, "add #{relative_filename}")
|
38
45
|
end
|
39
46
|
|
40
47
|
def transactional?
|
41
48
|
true
|
42
49
|
end
|
43
50
|
|
44
|
-
def checkout(
|
45
|
-
checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
|
46
|
-
mkdir_p(checkout_dir)
|
51
|
+
def checkout(to_identifier=nil)
|
52
|
+
checkout_dir = PathConverter.filepath_to_nativepath(@checkout_dir, false)
|
53
|
+
mkdir_p(@checkout_dir)
|
47
54
|
checked_out_files = []
|
48
55
|
path_regex = /^[A|D|U]\s+(.*)/
|
49
|
-
if(checked_out?
|
50
|
-
svn(checkout_dir, update_command(to_identifier)) do |line|
|
56
|
+
if(checked_out?)
|
57
|
+
svn(@checkout_dir, update_command(to_identifier)) do |line|
|
51
58
|
if(line =~ path_regex)
|
52
59
|
absolute_path = "#{checkout_dir}/#{$1}"
|
53
60
|
relative_path = $1.chomp
|
@@ -57,14 +64,15 @@ module RSCM
|
|
57
64
|
end
|
58
65
|
end
|
59
66
|
else
|
60
|
-
svn(checkout_dir, checkout_command(
|
67
|
+
svn(@checkout_dir, checkout_command(to_identifier)) do |line|
|
61
68
|
if(line =~ path_regex)
|
62
69
|
native_absolute_path = $1
|
63
|
-
|
64
|
-
absolute_path = PathConverter.nativepath_to_filepath($1)
|
65
|
-
native_checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
|
70
|
+
absolute_path = PathConverter.nativepath_to_filepath(native_absolute_path)
|
66
71
|
if(File.exist?(absolute_path) && !File.directory?(absolute_path))
|
67
|
-
|
72
|
+
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
|
68
76
|
relative_path = relative_path.gsub(/\\/, "/") if WINDOWS
|
69
77
|
checked_out_files << relative_path
|
70
78
|
yield relative_path if block_given?
|
@@ -83,60 +91,43 @@ module RSCM
|
|
83
91
|
"svn update #{url} #{checkout_dir}"
|
84
92
|
end
|
85
93
|
|
86
|
-
def uptodate?(
|
87
|
-
if(!checked_out?
|
94
|
+
def uptodate?(identifier)
|
95
|
+
if(!checked_out?)
|
88
96
|
false
|
89
97
|
else
|
90
|
-
|
91
|
-
|
92
|
-
lr == hr
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def local_revision(checkout_dir)
|
97
|
-
local_revision = nil
|
98
|
-
svn(checkout_dir, "info") do |line|
|
99
|
-
if(line =~ /Revision: ([0-9]*)/)
|
100
|
-
return $1.to_i
|
101
|
-
end
|
98
|
+
rev = identifier ? identifier : head_revision_identifier
|
99
|
+
local_revision_identifier == rev
|
102
100
|
end
|
103
101
|
end
|
104
102
|
|
105
|
-
def
|
106
|
-
|
107
|
-
with_working_dir(checkout_dir) do
|
108
|
-
safer_popen(cmd) do |stdout|
|
109
|
-
parser = SubversionLogParser.new(stdout, path, checkout_dir)
|
110
|
-
changesets = parser.parse_changesets
|
111
|
-
changesets[0].revision.to_i
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def commit(checkout_dir, message)
|
117
|
-
svn(checkout_dir, commit_command(message))
|
103
|
+
def commit(message)
|
104
|
+
svn(@checkout_dir, commit_command(message))
|
118
105
|
# We have to do an update to get the local revision right
|
119
|
-
svn(checkout_dir, "update")
|
106
|
+
svn(@checkout_dir, "update")
|
120
107
|
end
|
121
108
|
|
122
|
-
def label
|
123
|
-
|
109
|
+
def label
|
110
|
+
local_revision_identifier.to_s
|
124
111
|
end
|
125
112
|
|
126
|
-
def diff(
|
127
|
-
with_working_dir(checkout_dir) do
|
128
|
-
cmd = "svn diff -r #{
|
129
|
-
|
113
|
+
def diff(file, &block)
|
114
|
+
with_working_dir(@checkout_dir) do
|
115
|
+
cmd = "svn diff -r #{file.previous_native_revision_identifier}:#{file.native_revision_identifier} \"#{url}/#{file.path}\""
|
116
|
+
Better.popen(cmd) do |io|
|
130
117
|
return(yield(io))
|
131
118
|
end
|
132
119
|
end
|
133
120
|
end
|
134
121
|
|
135
|
-
def
|
122
|
+
def can_create_central?
|
136
123
|
local?
|
137
124
|
end
|
138
125
|
|
139
|
-
def
|
126
|
+
def destroy_central
|
127
|
+
FileUtils.rm_rf(svnrootdir)
|
128
|
+
end
|
129
|
+
|
130
|
+
def central_exists?
|
140
131
|
if(local?)
|
141
132
|
File.exists?("#{svnrootdir}/db")
|
142
133
|
else
|
@@ -145,7 +136,7 @@ module RSCM
|
|
145
136
|
# on stdout (and an error msg on std err).
|
146
137
|
exists = false
|
147
138
|
cmd = "svn log #{url} -r HEAD"
|
148
|
-
|
139
|
+
Better.popen(cmd) do |stdout|
|
149
140
|
stdout.each_line do |line|
|
150
141
|
exists = true
|
151
142
|
end
|
@@ -158,7 +149,7 @@ module RSCM
|
|
158
149
|
local?
|
159
150
|
end
|
160
151
|
|
161
|
-
def
|
152
|
+
def create_central
|
162
153
|
native_path = PathConverter.filepath_to_nativepath(svnrootdir, true)
|
163
154
|
mkdir_p(PathConverter.nativepath_to_filepath(native_path))
|
164
155
|
svnadmin(svnrootdir, "create #{native_path}")
|
@@ -182,27 +173,27 @@ module RSCM
|
|
182
173
|
not_already_commented
|
183
174
|
end
|
184
175
|
|
185
|
-
def
|
176
|
+
def import_central(dir, message)
|
186
177
|
import_cmd = "import #{url} -m \"#{message}\""
|
187
178
|
svn(dir, import_cmd)
|
188
179
|
end
|
189
180
|
|
190
|
-
def
|
191
|
-
# Return empty
|
192
|
-
return
|
181
|
+
def revisions(from_identifier, to_identifier=Time.infinity)
|
182
|
+
# Return empty revision if the requested revision doesn't exist yet.
|
183
|
+
return Revisions.new if(from_identifier.is_a?(Integer) && head_revision_identifier <= from_identifier)
|
193
184
|
|
194
|
-
checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
|
195
|
-
|
185
|
+
checkout_dir = PathConverter.filepath_to_nativepath(@checkout_dir, false)
|
186
|
+
revisions = nil
|
196
187
|
command = "svn #{changes_command(from_identifier, to_identifier)}"
|
197
188
|
yield command if block_given?
|
198
189
|
|
199
|
-
with_working_dir(checkout_dir) do
|
200
|
-
|
201
|
-
parser = SubversionLogParser.new(stdout,
|
202
|
-
|
190
|
+
with_working_dir(@checkout_dir) do
|
191
|
+
Better.popen(command) do |stdout|
|
192
|
+
parser = SubversionLogParser.new(stdout, @url)
|
193
|
+
revisions = parser.parse_revisions
|
203
194
|
end
|
204
195
|
end
|
205
|
-
|
196
|
+
revisions
|
206
197
|
end
|
207
198
|
|
208
199
|
# url pointing to the root of the repo
|
@@ -211,7 +202,7 @@ module RSCM
|
|
211
202
|
url[0..last]
|
212
203
|
end
|
213
204
|
|
214
|
-
def checked_out?
|
205
|
+
def checked_out?
|
215
206
|
rootentries = File.expand_path("#{checkout_dir}/.svn/entries")
|
216
207
|
result = File.exists?(rootentries)
|
217
208
|
result
|
@@ -219,6 +210,30 @@ module RSCM
|
|
219
210
|
|
220
211
|
private
|
221
212
|
|
213
|
+
def local_revision_identifier
|
214
|
+
local_revision_identifier = nil
|
215
|
+
svn(@checkout_dir, "info") do |line|
|
216
|
+
if(line =~ /Revision: ([0-9]*)/)
|
217
|
+
return $1.to_i
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def head_revision_identifier
|
223
|
+
# This command only seems to yield any changesets if the url is the root of
|
224
|
+
# the repo, which we don't know in the case where path is not specified (likely)
|
225
|
+
# We therefore don't specify it and get the latest revision from the full url instead.
|
226
|
+
# cmd = "svn log #{login_options} #{repourl} -r HEAD"
|
227
|
+
cmd = "svn log #{login_options} #{url}"
|
228
|
+
with_working_dir(@checkout_dir) do
|
229
|
+
Better.popen(cmd) do |stdout|
|
230
|
+
parser = SubversionLogParser.new(stdout, @url)
|
231
|
+
revisions = parser.parse_revisions
|
232
|
+
revisions[0].identifier.to_i
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
222
237
|
def install_unix_trigger(trigger_command, damagecontrol_install_dir)
|
223
238
|
post_commit_exists = File.exists?(post_commit_file)
|
224
239
|
mode = post_commit_exists ? File::APPEND|File::WRONLY : File::CREAT|File::WRONLY
|
@@ -227,7 +242,7 @@ module RSCM
|
|
227
242
|
file.puts("#!/bin/sh") unless post_commit_exists
|
228
243
|
file.puts("#{trigger_command}\n" )
|
229
244
|
end
|
230
|
-
|
245
|
+
File.chmod(0744, post_commit_file)
|
231
246
|
rescue
|
232
247
|
raise "Didn't have permission to write to #{post_commit_file}. " +
|
233
248
|
"Try to manually add the following line:\n\n#{trigger_command}\n\n" +
|
@@ -265,7 +280,7 @@ module RSCM
|
|
265
280
|
command_line = "#{executable} #{cmd}"
|
266
281
|
dir = File.expand_path(dir)
|
267
282
|
with_working_dir(dir) do
|
268
|
-
|
283
|
+
Better.popen(command_line) do |stdout|
|
269
284
|
stdout.each_line do |line|
|
270
285
|
yield line if block_given?
|
271
286
|
end
|
@@ -273,36 +288,50 @@ module RSCM
|
|
273
288
|
end
|
274
289
|
end
|
275
290
|
|
276
|
-
def checkout_command(
|
291
|
+
def checkout_command(to_identifier)
|
277
292
|
checkout_dir = "\"#{checkout_dir}\""
|
278
|
-
"checkout #{url} #{checkout_dir} #{revision_option(nil,to_identifier)}"
|
293
|
+
"checkout #{login_options} #{url} #{checkout_dir} #{revision_option(nil,to_identifier)}"
|
279
294
|
end
|
280
295
|
|
281
296
|
def update_command(to_identifier)
|
282
|
-
"update #{revision_option(nil,to_identifier)}"
|
297
|
+
"update #{login_options} #{revision_option(nil,to_identifier)}"
|
283
298
|
end
|
284
299
|
|
285
300
|
def changes_command(from_identifier, to_identifier)
|
286
301
|
# http://svnbook.red-bean.com/svnbook-1.1/svn-book.html#svn-ch-3-sect-3.3
|
287
302
|
# file_list = files.join('\n')
|
288
303
|
# WEIRD cygwin bug garbles this!?!?!?!
|
289
|
-
cmd = "log --verbose #{revision_option(from_identifier, to_identifier)} #{url}"
|
304
|
+
cmd = "log --verbose #{login_options} #{revision_option(from_identifier, to_identifier)} #{url}"
|
290
305
|
cmd
|
291
306
|
end
|
292
307
|
|
308
|
+
def login_options
|
309
|
+
result = ""
|
310
|
+
u = @username ? @username.strip : nil
|
311
|
+
p = @password ? @password.strip : nil
|
312
|
+
result << "--username #{u} " if u
|
313
|
+
result << "--password #{p} " if p
|
314
|
+
result
|
315
|
+
end
|
316
|
+
|
293
317
|
def revision_option(from_identifier, to_identifier)
|
318
|
+
# The inclusive start
|
294
319
|
from = nil
|
295
320
|
if(from_identifier.is_a?(Time))
|
296
|
-
from = svndate(from_identifier)
|
297
|
-
|
298
|
-
from = from_identifier
|
321
|
+
from = svndate(from_identifier + 1)
|
322
|
+
elsif(from_identifier.is_a?(Numeric))
|
323
|
+
from = from_identifier + 1
|
324
|
+
elsif(!from_identifier.nil?)
|
325
|
+
raise "from_identifier must be Numeric, Time or nil. Was: #{from_identifier} (#{from_identifier.class.name})"
|
299
326
|
end
|
300
327
|
|
301
328
|
to = nil
|
302
329
|
if(to_identifier.is_a?(Time))
|
303
330
|
to = svndate(to_identifier)
|
304
|
-
|
331
|
+
elsif(to_identifier.is_a?(Numeric))
|
305
332
|
to = to_identifier
|
333
|
+
elsif(!from_identifier.nil?)
|
334
|
+
raise "to_identifier must be Numeric, Time or nil. Was: #{to_identifier} (#{to_identifier.class.name})"
|
306
335
|
end
|
307
336
|
|
308
337
|
revision_option = nil
|
@@ -1,108 +1,137 @@
|
|
1
1
|
require 'rscm/parser'
|
2
|
-
require 'rscm/
|
2
|
+
require 'rscm/revision'
|
3
3
|
|
4
4
|
module RSCM
|
5
5
|
|
6
6
|
class SubversionLogParser
|
7
|
-
def initialize(io,
|
7
|
+
def initialize(io, url)
|
8
8
|
@io = io
|
9
|
-
@
|
9
|
+
@revision_parser = SubversionLogEntryParser.new(url)
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def parse_revisions(&line_proc)
|
13
13
|
# skip over the first ------
|
14
|
-
@
|
15
|
-
|
14
|
+
@revision_parser.parse(@io, true, &line_proc)
|
15
|
+
revisions = Revisions.new
|
16
16
|
while(!@io.eof?)
|
17
|
-
|
18
|
-
if(
|
19
|
-
|
17
|
+
revision = @revision_parser.parse(@io, &line_proc)
|
18
|
+
if(revision)
|
19
|
+
revisions.add(revision)
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
revisions
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
class SubversionLogEntryParser < Parser
|
27
27
|
|
28
|
-
def initialize(
|
28
|
+
def initialize(url)
|
29
29
|
super(/^------------------------------------------------------------------------/)
|
30
|
-
@
|
31
|
-
@checkout_dir = checkout_dir
|
30
|
+
@url = url
|
32
31
|
end
|
33
32
|
|
34
33
|
def parse(io, skip_line_parsing=false, &line_proc)
|
35
34
|
# We have to trim off the last newline - it's not meant to be part of the message
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
revision = super
|
36
|
+
revision.message = revision.message[0..-2] if revision
|
37
|
+
revision
|
39
38
|
end
|
40
39
|
|
40
|
+
def relative_path(url, repo_path)
|
41
|
+
url_tokens = url.split('/')
|
42
|
+
repo_path_tokens = repo_path.split('/')
|
43
|
+
|
44
|
+
max_similar = repo_path_tokens.length
|
45
|
+
while(max_similar > 0)
|
46
|
+
url = url_tokens[-max_similar..-1]
|
47
|
+
path = repo_path_tokens[0..max_similar-1]
|
48
|
+
if(url == path)
|
49
|
+
break
|
50
|
+
end
|
51
|
+
max_similar -= 1
|
52
|
+
end
|
53
|
+
max_similar == 0 ? nil : repo_path_tokens[max_similar..-1].join("/")
|
54
|
+
end
|
55
|
+
|
41
56
|
protected
|
42
57
|
|
43
58
|
def parse_line(line)
|
44
|
-
if(@
|
59
|
+
if(@revision.nil?)
|
45
60
|
parse_header(line)
|
46
61
|
elsif(line.strip == "")
|
47
62
|
@parse_state = :parse_message
|
48
63
|
elsif(line =~ /Changed paths/)
|
49
|
-
@parse_state = :
|
50
|
-
elsif(@parse_state == :
|
51
|
-
|
52
|
-
if
|
53
|
-
|
54
|
-
|
55
|
-
|
64
|
+
@parse_state = :parse_files
|
65
|
+
elsif(@parse_state == :parse_files)
|
66
|
+
file = parse_file(line)
|
67
|
+
if(file)
|
68
|
+
previously_added_file = @revision[-1]
|
69
|
+
if(previously_added_file)
|
70
|
+
# remove previous revision_file it if it's a dir
|
71
|
+
previous_tokens = previously_added_file.path.split("/")
|
72
|
+
current_tokens = file.path.split("/")
|
73
|
+
current_tokens.pop
|
74
|
+
if(previous_tokens == current_tokens)
|
75
|
+
@revision.pop
|
76
|
+
end
|
77
|
+
end
|
78
|
+
@revision << file
|
56
79
|
end
|
57
80
|
elsif(@parse_state == :parse_message)
|
58
|
-
@
|
81
|
+
@revision.message << line.chomp << "\n"
|
59
82
|
end
|
60
83
|
end
|
61
84
|
|
62
85
|
def next_result
|
63
|
-
result = @
|
64
|
-
@
|
86
|
+
result = @revision
|
87
|
+
@revision = nil
|
65
88
|
result
|
66
89
|
end
|
67
90
|
|
68
91
|
private
|
69
92
|
|
70
|
-
STATES = {"M" =>
|
93
|
+
STATES = {"M" => RevisionFile::MODIFIED, "A" => RevisionFile::ADDED, "D" => RevisionFile::DELETED} unless defined? STATES
|
71
94
|
|
72
95
|
def parse_header(line)
|
73
|
-
@
|
74
|
-
@
|
96
|
+
@revision = Revision.new
|
97
|
+
@revision.message = ""
|
75
98
|
revision, developer, time, the_rest = line.split("|")
|
76
|
-
@
|
77
|
-
@
|
78
|
-
@
|
99
|
+
@revision.identifier = revision.strip[1..-1].to_i unless revision.nil?
|
100
|
+
@revision.developer = developer.strip unless developer.nil?
|
101
|
+
@revision.time = parse_time(time.strip) unless time.nil?
|
79
102
|
end
|
80
103
|
|
81
|
-
def
|
82
|
-
|
104
|
+
def parse_file(line)
|
105
|
+
file = RevisionFile.new
|
83
106
|
path_from_root = nil
|
84
107
|
if(line =~ /^ [M|A|D|R] ([^\s]+) \(from (.*)\)/)
|
85
108
|
path_from_root = $1
|
86
|
-
|
109
|
+
file.status = RevisionFile::MOVED
|
87
110
|
elsif(line =~ /^ ([M|A|D|R]) (.+)$/)
|
88
111
|
status = $1
|
89
112
|
path_from_root = $2
|
90
|
-
|
113
|
+
file.status = STATES[status]
|
91
114
|
else
|
92
|
-
raise "could not parse
|
115
|
+
raise "could not parse file line: '#{line}'"
|
93
116
|
end
|
94
117
|
|
95
118
|
path_from_root.gsub!(/\\/, "/")
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
119
|
+
path_from_root = path_from_root[1..-1]
|
120
|
+
rp = relative_path(@url, path_from_root)
|
121
|
+
return if rp.nil?
|
122
|
+
file.path = rp
|
123
|
+
|
124
|
+
|
125
|
+
# if(@path.length+1 == path_from_root.length)
|
126
|
+
# file.path = path_from_root[@path.length+1..-1]
|
127
|
+
# else
|
128
|
+
# file.path = path_from_root[@path.length+2..-1]
|
129
|
+
# end
|
130
|
+
|
131
|
+
file.native_revision_identifier = @revision.identifier
|
103
132
|
# http://jira.codehaus.org/browse/DC-204
|
104
|
-
|
105
|
-
|
133
|
+
file.previous_native_revision_identifier = file.native_revision_identifier.to_i - 1;
|
134
|
+
file
|
106
135
|
end
|
107
136
|
|
108
137
|
def parse_time(svn_time)
|