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.
Files changed (50) hide show
  1. data/README +34 -23
  2. data/Rakefile +24 -29
  3. data/bin/touch.exe +0 -0
  4. data/lib/rscm.rb +6 -3
  5. data/lib/rscm/annotations.rb +26 -7
  6. data/lib/rscm/{abstract_scm.rb → base.rb} +109 -71
  7. data/lib/rscm/better.rb +16 -0
  8. data/lib/rscm/logging.rb +11 -5
  9. data/lib/rscm/path_converter.rb +9 -16
  10. data/lib/rscm/revision.rb +201 -0
  11. data/lib/rscm/revision_file.rb +71 -0
  12. data/lib/rscm/scm/clearcase.rb +7 -7
  13. data/lib/rscm/scm/cvs.rb +69 -70
  14. data/lib/rscm/scm/cvs_log_parser.rb +29 -29
  15. data/lib/rscm/scm/darcs.rb +82 -34
  16. data/lib/rscm/scm/darcs_log_parser.rb +65 -0
  17. data/lib/rscm/scm/monotone.rb +249 -77
  18. data/lib/rscm/scm/monotone_log_parser.rb +57 -43
  19. data/lib/rscm/scm/mooky.rb +3 -3
  20. data/lib/rscm/scm/perforce.rb +196 -134
  21. data/lib/rscm/scm/star_team.rb +10 -10
  22. data/lib/rscm/scm/subversion.rb +106 -77
  23. data/lib/rscm/scm/subversion_log_parser.rb +76 -47
  24. data/lib/rscm/time_ext.rb +2 -116
  25. data/test/rscm/annotations_test.rb +15 -2
  26. data/test/rscm/{abstract_scm_test.rb → base_test.rb} +3 -3
  27. data/test/rscm/difftool_test.rb +9 -3
  28. data/test/rscm/generic_scm_tests.rb +195 -124
  29. data/test/rscm/revision_fixture.rb +20 -0
  30. data/test/rscm/revision_test.rb +129 -0
  31. data/test/rscm/{changesets.yaml → revisions.yaml} +10 -10
  32. data/test/rscm/scm/clearcase.log +608 -0
  33. data/test/rscm/scm/clearcase_test.rb +39 -0
  34. data/test/rscm/scm/cvs_log_parser_test.rb +73 -73
  35. data/test/rscm/scm/cvs_test.rb +1 -1
  36. data/test/rscm/scm/darcs_log_parser_test.rb +171 -0
  37. data/test/rscm/scm/monotone_log_parser_test.rb +49 -31
  38. data/test/rscm/scm/monotone_test.rb +3 -2
  39. data/test/rscm/scm/p4client_test.rb +33 -0
  40. data/test/rscm/scm/perforce_test.rb +25 -3
  41. data/test/rscm/scm/star_team.rb +9 -9
  42. data/test/rscm/scm/subversion_log_parser_test.rb +107 -47
  43. metadata +17 -13
  44. data/lib/multipart.rb +0 -95
  45. data/lib/rscm/RSS.txt +0 -41
  46. data/lib/rscm/changes.rb +0 -268
  47. data/lib/rscm/example.yaml +0 -21
  48. data/lib/rubyforge_file_publisher.rb +0 -176
  49. data/test/rscm/changes_fixture.rb +0 -20
  50. data/test/rscm/changes_test.rb +0 -129
@@ -6,49 +6,56 @@ module RSCM
6
6
 
7
7
  class MonotoneLogParser
8
8
 
9
- def parse_changesets(io, from_identifier=Time.epoch, to_identifier=Time.infinity)
9
+ def parse_revisions(io, from_identifier=Time.epoch, to_identifier=Time.infinity)
10
10
  # skip first separator
11
11
  io.readline
12
12
 
13
- changesets = ChangeSets.new
14
- changeset_string = ""
13
+ all_revisions = []
14
+ revision_string = ""
15
15
 
16
16
  # hash of path => [array of revisions]
17
17
  path_revisions = {}
18
18
  io.each_line do |line|
19
19
  if(line =~ /-----------------------------------------------------------------/)
20
- changeset = parse_changeset(StringIO.new(changeset_string), path_revisions)
21
- changesets.add(changeset)
22
- changeset_string = ""
20
+ revision = parse_revision(StringIO.new(revision_string), path_revisions)
21
+ all_revisions << revision
22
+ revision_string = ""
23
23
  else
24
- changeset_string << line
24
+ revision_string << line
25
25
  end
26
26
  end
27
- changeset = parse_changeset(StringIO.new(changeset_string), path_revisions)
28
- if((from_identifier <= changeset.time) && (changeset.time <= to_identifier))
29
- changesets.add(changeset)
30
- end
31
-
32
- # set the previous revisions. most recent is at index 0.
33
- changesets.each do |changeset|
34
- changeset.each do |change|
35
- current_index = path_revisions[change.path].index(change.revision)
36
- change.previous_revision = path_revisions[change.path][current_index + 1]
27
+ revision = parse_revision(StringIO.new(revision_string), path_revisions)
28
+ all_revisions << revision
29
+
30
+ # Filter out the revisions and set the previous revisions, knowing that most recent is at index 0.
31
+
32
+ from_time = time(all_revisions, from_identifier, Time.epoch)
33
+ to_time = time(all_revisions, to_identifier, Time.infinity)
34
+
35
+ revisions = Revisions.new
36
+
37
+ all_revisions.each do |revision|
38
+ if((from_time < revision.time) && (revision.time <= to_time))
39
+ revisions.add(revision)
40
+ revision.each do |change|
41
+ current_index = path_revisions[change.path].index(change.native_revision_identifier)
42
+ change.previous_native_revision_identifier = path_revisions[change.path][current_index + 1]
43
+ end
37
44
  end
38
45
  end
39
- changesets
40
- end
41
-
42
- def parse_changeset(changeset_io, path_revisions)
43
- changeset = ChangeSet.new
46
+ revisions
47
+ end
48
+
49
+ def parse_revision(revision_io, path_revisions)
50
+ revision = Revision.new
44
51
  state = nil
45
- changeset_io.each_line do |line|
46
- if(line =~ /^Revision: (.*)$/ && changeset.revision.nil?)
47
- changeset.revision = $1
48
- elsif(line =~ /^Author: (.*)$/ && changeset.developer.nil?)
49
- changeset.developer = $1
50
- elsif(line =~ /^Date: (.*)$/ && changeset.time.nil?)
51
- changeset.time = Time.utc(
52
+ revision_io.each_line do |line|
53
+ if(line =~ /^Revision: (.*)$/ && revision.identifier.nil?)
54
+ revision.identifier = $1
55
+ elsif(line =~ /^Author: (.*)$/ && revision.developer.nil?)
56
+ revision.developer = $1
57
+ elsif(line =~ /^Date: (.*)$/ && revision.time.nil?)
58
+ revision.time = Time.utc(
52
59
  $1[0..3].to_i,
53
60
  $1[5..6].to_i,
54
61
  $1[8..9].to_i,
@@ -56,37 +63,44 @@ module RSCM
56
63
  $1[14..15].to_i,
57
64
  $1[17..18].to_i
58
65
  )
59
- elsif(line =~ /^ChangeLog:$/ && changeset.message.nil?)
66
+ elsif(line =~ /^ChangeLog:\s*$/ && revision.message.nil?)
60
67
  state = :message
61
- elsif(state == :message && changeset.message.nil?)
62
- changeset.message = ""
63
- elsif(state == :message && changeset.message)
64
- changeset.message << line
65
- elsif(line =~ /^Added files:$/)
68
+ elsif(state == :message && revision.message.nil?)
69
+ revision.message = ""
70
+ elsif(state == :message && revision.message)
71
+ revision.message << line
72
+ elsif(line =~ /^Added files:\s*$/)
66
73
  state = :added
67
74
  elsif(state == :added)
68
- add_changes(changeset, line, Change::ADDED, path_revisions)
69
- elsif(line =~ /^Modified files:$/)
75
+ add_changes(revision, line, RevisionFile::ADDED, path_revisions)
76
+ elsif(line =~ /^Modified files:\s*$/)
70
77
  state = :modified
71
78
  elsif(state == :modified)
72
- add_changes(changeset, line, Change::MODIFIED, path_revisions)
79
+ add_changes(revision, line, RevisionFile::MODIFIED, path_revisions)
73
80
  end
74
81
  end
75
- changeset.message.chomp!
76
- changeset
82
+ revision.message.chomp! rescue revision.message = ''
83
+ revision
77
84
  end
78
85
 
79
86
  private
80
87
 
81
- def add_changes(changeset, line, state, path_revisions)
88
+ def time(revisions, identifier, default)
89
+ cs = revisions.find do |revision|
90
+ revision.identifier == identifier
91
+ end
92
+ cs ? cs.time : (identifier.is_a?(Time) ? identifier : default)
93
+ end
94
+
95
+ def add_changes(revision, line, state, path_revisions)
82
96
  paths = line.split(" ")
83
97
  paths.each do |path|
84
- changeset << Change.new(path, state, changeset.developer, nil, changeset.revision, changeset.time)
98
+ revision << RevisionFile.new(path, state, revision.developer, nil, revision.identifier, revision.time)
85
99
 
86
100
  # now record path revisions so we can keep track of previous rev for each path
87
101
  # doesn't work for moved files, and have no idea how to make it work either.
88
102
  path_revisions[path] ||= []
89
- path_revisions[path] << changeset.revision
103
+ path_revisions[path] << revision.identifier
90
104
  end
91
105
 
92
106
  end
@@ -1,8 +1,8 @@
1
- require 'rscm/abstract_scm'
1
+ require 'rscm/base'
2
2
 
3
3
  module RSCM
4
- class Mooky < AbstractSCM
5
- register self
4
+ class Mooky < Base
5
+ #register self
6
6
 
7
7
  ann :description => "The Foo", :tip => "Foo is nonsense"
8
8
  attr_accessor :foo
@@ -1,4 +1,4 @@
1
- require 'rscm/abstract_scm'
1
+ require 'rscm/base'
2
2
  require 'rscm/path_converter'
3
3
  require 'rscm/line_editor'
4
4
 
@@ -9,26 +9,64 @@ require 'parsedate'
9
9
  require 'stringio'
10
10
 
11
11
  module RSCM
12
- # RSCM implementation for Perforce.
12
+ # Perforce RSCM implementation.
13
13
  #
14
14
  # Understands operations against multiple client-workspaces
15
15
  # You need the p4/p4d executable on the PATH in order for it to work.
16
16
  #
17
- class Perforce < AbstractSCM
17
+ class Perforce < Base
18
18
  register self
19
19
 
20
20
  include FileUtils
21
21
 
22
- ann :description => "Depot path", :tip => "The path to the Perforce depot"
23
- attr_accessor :depotpath
22
+ @@counter = 0
23
+
24
+ ann :description => "P4CLIENT: workspace name"
25
+ attr_accessor :client_name
26
+
27
+ ann :description => "P4PORT: [host:port]", :tip => "perforce server address e.g. 10.12.1.55:1666"
28
+ attr_accessor :port
29
+
30
+ ann :description => "P4USER", :tip => "username"
31
+ attr_accessor :user
32
+
33
+ ann :description => "P4PASSWD", :tip => "password"
34
+ attr_accessor :pwd
35
+
36
+ attr_accessor :repository_root_dir
24
37
 
25
- def initialize(repository_root_dir = "")
26
- @clients = {}
27
- @depotpath = repository_root_dir
38
+ def initialize(port = "1666", user = ENV["LOGNAME"], pwd = "", client_name = Perforce.next_client_name)
39
+ @port, @user, @pwd, @client_name = port, user, pwd, client_name
28
40
  end
29
41
 
30
- def create
31
- P4Daemon.new(@depotpath).start
42
+ def p4admin
43
+ @p4admin ||= P4Admin.new(@port, @user, @pwd)
44
+ end
45
+
46
+ def p4client
47
+ @p4client ||= p4admin.create_client(@checkout_dir, @client_name)
48
+ end
49
+
50
+ def can_create_central?
51
+ true
52
+ end
53
+
54
+ def create_central
55
+ raise "perforce depot can be created only from tests" unless @repository_root_dir
56
+ @p4d = P4Daemon.new(@repository_root_dir)
57
+ @p4d.start
58
+ end
59
+
60
+ def destroy_central
61
+ @p4d.shutdown
62
+ end
63
+
64
+ def central_exists?
65
+ p4admin.central_exists?
66
+ end
67
+
68
+ def can_create_central?
69
+ true
32
70
  end
33
71
 
34
72
  def name
@@ -39,35 +77,35 @@ module RSCM
39
77
  true
40
78
  end
41
79
 
42
- def import(dir, comment)
80
+ def import_central(dir, comment)
43
81
  with_create_client(dir) do |client|
44
82
  client.add_all(list_files)
45
83
  client.submit(comment)
46
84
  end
47
85
  end
48
86
 
49
- def checkout(checkout_dir, to_identifier = nil, &proc)
50
- client(checkout_dir).checkout(to_identifier, &proc)
87
+ def checkout(to_identifier = nil, &proc)
88
+ p4client.checkout(to_identifier, &proc)
51
89
  end
52
90
 
53
- def add(checkout_dir, relative_filename)
54
- client(checkout_dir).add(relative_filename)
91
+ def add(relative_filename)
92
+ p4client.add(relative_filename)
55
93
  end
56
94
 
57
- def commit(checkout_dir, message, &proc)
58
- client(checkout_dir).submit(message, &proc)
95
+ def commit(message, &proc)
96
+ p4client.submit(message, &proc)
59
97
  end
60
98
 
61
- def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
62
- client(checkout_dir).changesets(from_identifier, to_identifier)
99
+ def revisions(from_identifier, to_identifier=Time.infinity)
100
+ p4client.revisions(from_identifier, to_identifier)
63
101
  end
64
102
 
65
- def uptodate?(checkout_dir, from_identifier)
66
- client(checkout_dir).uptodate?
103
+ def uptodate?(from_identifier)
104
+ p4client.uptodate?
67
105
  end
68
106
 
69
107
  def edit(file)
70
- client_containing(file).edit(file)
108
+ p4client.edit(file)
71
109
  end
72
110
 
73
111
  def trigger_installed?(trigger_command, trigger_files_checkout_dir)
@@ -82,25 +120,18 @@ module RSCM
82
120
  p4admin.uninstall_trigger(trigger_command)
83
121
  end
84
122
 
85
- private
86
-
87
- def p4admin
88
- @p4admin ||= P4Admin.new
123
+ def diff(revfile, &proc)
124
+ p4client.diff(revfile, &proc)
89
125
  end
90
126
 
91
- def client_containing(path)
92
- @clients.values.find {|client| client.contains?(path)}
93
- end
94
-
95
- def client(rootdir)
96
- @clients[rootdir] ||= create_client(rootdir)
97
- end
127
+ private
98
128
 
99
129
  def with_create_client(rootdir)
100
130
  raise "needs a block" unless block_given?
101
131
  rootdir = File.expand_path(rootdir)
102
132
  with_working_dir(rootdir) do
103
- client = create_client(rootdir)
133
+ mkdir_p(rootdir)
134
+ client = p4admin.create_client(rootdir, Perforce.next_client_name)
104
135
  begin
105
136
  yield client
106
137
  ensure
@@ -109,70 +140,43 @@ module RSCM
109
140
  end
110
141
  end
111
142
 
112
- def delete_client(client)
113
- p4admin.delete_client(client.name)
143
+ def self.next_client_name
144
+ "temp_client_#{@@counter += 1}"
114
145
  end
115
146
 
116
- def create_client(rootdir)
117
- rootdir = File.expand_path(rootdir) if rootdir =~ /\.\./
118
- mkdir_p(rootdir)
119
- p4admin.create_client(rootdir)
147
+ def delete_client(client)
148
+ p4admin.delete_client(client)
120
149
  end
121
150
 
122
151
  def list_files
123
152
  files = Dir["**/*"].delete_if{|f| File.directory?(f)}
124
153
  files.collect{|f| File.expand_path(f)}
125
154
  end
155
+ end
126
156
 
127
- class P4Daemon
128
- include FileUtils
129
-
130
- def initialize(depotpath)
131
- @depotpath = depotpath
132
- end
133
-
134
- def start
135
- shutdown if running?
136
- launch
137
- assert_running
138
- end
139
-
140
- def assert_running
141
- raise "p4d did not start properly" if timeout(10) { running? }
142
- end
143
-
144
- def launch
145
- fork do
146
- mkdir_p(@depotpath)
147
- cd(@depotpath)
148
- debug "starting p4 server"
149
- exec("p4d")
150
- end
151
- at_exit { shutdown }
152
- end
157
+ # Understands p4 administrative operations (not specific to a client)
158
+ class P4Admin
153
159
 
154
- def shutdown
155
- `p4 -p 1666 admin stop`
156
- end
160
+ def initialize(port, user, pwd)
161
+ @port, @user, @pwd = port, user, pwd
162
+ end
157
163
 
158
- def running?
159
- !`p4 -p 1666 info`.empty?
164
+ def create_client(rootdir, clientname)
165
+ rootdir = File.expand_path(rootdir) if rootdir =~ /\.\./
166
+ unless client_exists?(rootdir, clientname)
167
+ execute_popen("client -i", "w+", clientspec(clientname, rootdir))
160
168
  end
169
+ P4Client.new(rootdir, clientname, @port, @user, @pwd)
161
170
  end
162
- end
163
-
164
- # Understands p4 administrative operations (not specific to a client)
165
- class P4Admin
166
- @@counter = 0
167
171
 
168
- def create_client(rootdir)
169
- name = next_name
170
- popen("client -i", "w+", clientspec(name, rootdir))
171
- P4Client.new(name, rootdir)
172
+ def client_exists?(rootdir, clientname)
173
+ dir_regex = Regexp.new(rootdir)
174
+ name_regex = Regexp.new(clientname)
175
+ execute("clients").split("\n").find {|c| c =~ dir_regex && c =~ name_regex}
172
176
  end
173
177
 
174
- def delete_client(name)
175
- execute("client -d #{name}")
178
+ def delete_client(client)
179
+ execute("client -d #{client.name}")
176
180
  end
177
181
 
178
182
  def trigger_installed?(trigger_command)
@@ -180,22 +184,26 @@ module RSCM
180
184
  end
181
185
 
182
186
  def install_trigger(trigger_command)
183
- popen("triggers -i", "a+", triggerspec_with(trigger_command))
187
+ execute_popen("triggers -i", "a+", triggerspec_append(trigger_command))
184
188
  end
185
189
 
186
190
  def uninstall_trigger(trigger_command)
187
- popen("triggers -i", "a+", triggerspec_without(trigger_command))
191
+ execute_popen("triggers -i", "a+", triggerspec_remove(trigger_command))
188
192
  end
189
193
 
190
- def triggerspec_with(trigger_command)
194
+ def triggerspec_append(trigger_command)
191
195
  new_trigger = " damagecontrol commit //depot/... \"#{trigger_command}\" "
192
196
  triggers + $/ + new_trigger
193
197
  end
194
198
 
195
- def triggerspec_without(trigger_command)
199
+ def triggerspec_remove(trigger_command)
196
200
  triggers.reject {|line| line =~ /#{trigger_command}/}.join
197
201
  end
198
202
 
203
+ def central_exists?
204
+ execute("info").split.join(" ") !~ /Connect to server failed/
205
+ end
206
+
199
207
  def clientspec(name, rootdir)
200
208
  s = StringIO.new
201
209
  s.puts "Client: #{name}"
@@ -213,8 +221,8 @@ module RSCM
213
221
  execute("triggers -o")
214
222
  end
215
223
 
216
- def popen(cmd, mode, input)
217
- IO.popen("p4 -p 1666 #{cmd}", mode) do |io|
224
+ def execute_popen(cmd, mode, input)
225
+ IO.popen(format_cmd(cmd), mode) do |io|
218
226
  io.puts(input)
219
227
  io.close_write
220
228
  io.each_line {|line| debug(line)}
@@ -222,41 +230,38 @@ module RSCM
222
230
  end
223
231
 
224
232
  def execute(cmd)
225
- cmd = "p4 -p 1666 #{cmd}"
226
- puts "> executing: #{cmd}"
233
+ cmd = format_cmd(cmd)
234
+ $stderr.puts "> executing: #{cmd}"
227
235
  `#{cmd}`
228
236
  end
229
237
 
230
- def next_name
231
- "client#{@@counter += 1}"
238
+ def format_cmd(cmd)
239
+ "p4 -p #{@port} -u '#{@user}' -P '#{@pwd}' #{cmd} 2>&1"
232
240
  end
233
241
  end
234
242
 
235
243
  # Understands operations against a client-workspace
236
244
  class P4Client
237
245
  DATE_FORMAT = "%Y/%m/%d:%H:%M:%S"
238
- STATUS = { "add" => Change::ADDED, "edit" => Change::MODIFIED, "delete" => Change::DELETED }
239
- PERFORCE_EPOCH = Time.utc(1970, 1, 1, 6, 0, 1) #perforce doesn't like Time.utc(1970)
240
-
241
- attr_accessor :name, :rootdir
242
-
243
- def initialize(name, rootdir)
244
- @name = name
245
- @rootdir = rootdir
246
- end
246
+ STATUS = { "add" => RevisionFile::ADDED, "edit" => RevisionFile::MODIFIED, "delete" => RevisionFile::DELETED }
247
247
 
248
- def contains?(file)
249
- file = File.expand_path(file)
250
- file =~ /^#{@rootdir}/
248
+ def initialize(checkout_dir, name, port, user, pwd)
249
+ @checkout_dir, @name, @port, @user, @pwd = checkout_dir, name, port, user, pwd
251
250
  end
252
251
 
253
252
  def uptodate?
254
253
  p4("sync -n").empty?
255
254
  end
256
255
 
257
- def changesets(from_identifier, to_identifier)
258
- changesets = changelists(from_identifier, to_identifier).collect {|changelist| to_changeset(changelist)}
259
- ChangeSets.new(changesets)
256
+ def revisions(from_identifier, to_identifier)
257
+ revisions = changelists(from_identifier, to_identifier).collect {|changelist| to_revision(changelist)}
258
+ # We have to reverse the revisions in order to make them appear in chronological order,
259
+ # P4 lists the newest ones first.
260
+ Revisions.new(revisions).reverse
261
+ end
262
+
263
+ def name
264
+ @name
260
265
  end
261
266
 
262
267
  def edit(file)
@@ -265,7 +270,7 @@ module RSCM
265
270
  end
266
271
 
267
272
  def add(relative_path)
268
- add_file(@rootdir + "/" + relative_path)
273
+ add_file(rootdir + "/" + relative_path)
269
274
  end
270
275
 
271
276
  def add_all(files)
@@ -284,8 +289,8 @@ module RSCM
284
289
  cmd = to_identifier.nil? ? "sync" : "sync //...@#{to_identifier}"
285
290
  checked_out_files = []
286
291
  p4(cmd).collect do |output|
287
- puts "output: '#{output}'"
288
- if(output =~ /.* - (added as|updating|deleted as) #{@rootdir}[\/|\\](.*)/)
292
+ #puts "output: '#{output}'"
293
+ if(output =~ /.* - (added as|updating|deleted as) #{rootdir}[\/|\\](.*)/)
289
294
  path = $2.gsub(/\\/, "/")
290
295
  checked_out_files << path
291
296
  yield path if block_given?
@@ -294,8 +299,26 @@ module RSCM
294
299
  checked_out_files
295
300
  end
296
301
 
302
+ def diff(r)
303
+ path = File.expand_path(@checkout_dir + "/" + r.path)
304
+ from = r.previous_native_revision_identifier
305
+ to = r.native_revision_identifier
306
+ cmd = p4cmd("diff2 -du #{path}@#{from} #{path}@#{to}")
307
+ Better.popen(cmd) do |io|
308
+ return(yield(io))
309
+ end
310
+ end
311
+
297
312
  private
298
313
 
314
+ def rootdir
315
+ unless @rootdir
316
+ p4("info") =~ /Client root: (.+)/
317
+ @rootdir = $1
318
+ end
319
+ @rootdir
320
+ end
321
+
299
322
  def add_file(absolute_path)
300
323
  absolute_path = PathConverter.filepath_to_nativepath(absolute_path, true)
301
324
  p4("add #{absolute_path}")
@@ -310,48 +333,53 @@ module RSCM
310
333
  end
311
334
  end
312
335
 
313
- def to_changeset(changelist)
336
+ def to_revision(changelist)
314
337
  return nil if changelist.nil? # Ugly, but it seems to be nil some times on windows.
315
338
  changes = changelist.files.collect do |filespec|
316
- change = Change.new(filespec.path, changelist.developer, changelist.message, filespec.revision, changelist.time)
339
+ change = RevisionFile.new(filespec.path, changelist.developer, changelist.message, filespec.revision, changelist.time)
317
340
  change.status = STATUS[filespec.status]
318
- change.previous_revision = filespec.revision - 1
341
+ change.previous_native_revision_identifier = filespec.revision - 1
319
342
  change
320
343
  end
321
- changeset = ChangeSet.new(changes)
322
- changeset.revision = changelist.number
323
- changeset.developer = changelist.developer
324
- changeset.message = changelist.message
325
- changeset.time = changelist.time
326
- changeset
344
+ revision = Revision.new(changes)
345
+ revision.identifier = changelist.number
346
+ revision.developer = changelist.developer
347
+ revision.message = changelist.message
348
+ revision.time = changelist.time
349
+ revision
327
350
  end
328
351
 
329
- def p4describe(chnum)
330
- p4("describe -s #{chnum}")
352
+ def p4changes(from_identifier, to_identifier)
353
+ from = p4timespec(from_identifier, Time.epoch)
354
+ to = p4timespec(to_identifier, Time.infinity)
355
+ $stderr.puts "in p4changes translated #{from_identifier},#{to_identifier} to #{from},#{to}"
356
+ p4("changes //...@#{from},#{to}")
331
357
  end
332
358
 
333
- def p4changes(from_identifier, to_identifier)
334
- if from_identifier.nil? || from_identifier.is_a?(Time)
335
- from_identifier = PERFORCE_EPOCH if from_identifier.nil? || from_identifier < PERFORCE_EPOCH
336
- to_identifier = Time.infinity if to_identifier.nil?
337
- from = from_identifier.strftime(DATE_FORMAT)
338
- to = to_identifier.strftime(DATE_FORMAT)
339
- p4("changes //...@#{from},#{to}")
359
+ def p4timespec(identifier, default)
360
+ identifier = default if identifier.nil?
361
+ if identifier.is_a?(Time)
362
+ identifier = Time.epoch if identifier < Time.epoch
363
+ (identifier+1).strftime(DATE_FORMAT)
340
364
  else
341
- p4("changes //...@#{from_identifier},#{from_identifier}")
365
+ "#{identifier + 1}"
342
366
  end
343
367
  end
344
368
 
369
+ def p4describe(chnum)
370
+ p4("describe -s #{chnum}")
371
+ end
372
+
345
373
  def p4(cmd)
346
374
  cmd = "#{p4cmd(cmd)}"
347
- puts "> executing: #{cmd}"
375
+ $stderr.puts "> executing: #{cmd}"
348
376
  output = `#{cmd}`
349
- puts output
377
+ #puts output
350
378
  output
351
379
  end
352
380
 
353
381
  def p4cmd(cmd)
354
- "p4 -p 1666 -c #{@name} #{cmd}"
382
+ "p4 -p #{@port} -c '#{@name}' -u '#{@user}' -P '#{@pwd}' #{cmd}"
355
383
  end
356
384
 
357
385
  def submitspec(comment)
@@ -377,7 +405,7 @@ module RSCM
377
405
  def initialize(log)
378
406
  debug log
379
407
  if(log =~ /^Change (\d+) by (.*) on (.*)$/)
380
- # @number, @developer, @time = $1.to_i, $2, Time.utc(*ParseDate.parsedate($3)[0..5])
408
+ #@number, @developer, @time = $1.to_i, $2, Time.utc(*ParseDate.parsedate($3)[0..5])
381
409
  @number, @developer, @time = $1.to_i, $2, Time.utc(*ParseDate.parsedate($3))
382
410
  else
383
411
  raise "Bad log format: '#{log}'"
@@ -397,6 +425,40 @@ module RSCM
397
425
  end
398
426
  end
399
427
 
428
+ class P4Daemon
429
+ include FileUtils
430
+
431
+ def initialize(depotpath)
432
+ @depotpath = depotpath
433
+ end
434
+
435
+ def start
436
+ launch
437
+ assert_running
438
+ end
439
+
440
+ def assert_running
441
+ raise "p4d did not start properly" if timeout(10) { running? }
442
+ end
443
+
444
+ def launch
445
+ fork do
446
+ mkdir_p(@depotpath)
447
+ cd(@depotpath)
448
+ debug "starting p4 server"
449
+ exec("p4d")
450
+ end
451
+ at_exit { shutdown }
452
+ end
453
+
454
+ def shutdown
455
+ `p4 -p 1666 admin stop` if running?
456
+ end
457
+
458
+ def running?
459
+ `p4 -p 1666 info 2>&1`!~ /Connect to server failed/
460
+ end
461
+ end
400
462
  end
401
463
 
402
464
  module Kernel
@@ -412,7 +474,7 @@ module Kernel
412
474
 
413
475
  #todo: replace with logger
414
476
  def debug(msg)
415
- puts msg
477
+ #puts msg
416
478
  end
417
479
 
418
480
  end