rscm 0.1.0.1338 → 0.2.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 +13 -28
- data/Rakefile +25 -24
- data/lib/rscm.rb +1 -6
- data/lib/rscm/abstract_scm.rb +36 -10
- data/lib/rscm/annotations.rb +50 -0
- data/lib/rscm/changes.rb +2 -5
- data/lib/rscm/logging.rb +1 -0
- data/lib/rscm/path_converter.rb +3 -2
- data/lib/rscm/{cvs → scm}/cvs.rb +16 -9
- data/lib/rscm/{cvs → scm}/cvs_log_parser.rb +4 -4
- data/lib/rscm/{darcs → scm}/darcs.rb +6 -3
- data/lib/rscm/scm/monotone.rb +162 -0
- data/lib/rscm/scm/monotone_log_parser.rb +95 -0
- data/lib/rscm/scm/mooky.rb +21 -0
- data/lib/rscm/{perforce → scm}/perforce.rb +7 -4
- data/lib/rscm/{starteam/starteam.rb → scm/star_team.rb} +23 -3
- data/lib/rscm/{svn/svn.rb → scm/subversion.rb} +17 -10
- data/lib/rscm/{svn/svn_log_parser.rb → scm/subversion_log_parser.rb} +8 -7
- data/test/rscm/abstract_scm_test.rb +21 -0
- data/test/rscm/annotations_test.rb +57 -0
- data/test/rscm/changes_fixture.rb +7 -7
- data/test/rscm/changes_test.rb +3 -3
- data/test/rscm/generic_scm_tests.rb +2 -2
- data/test/rscm/{cvs → scm}/cvs-dataforge.log +0 -0
- data/test/rscm/{cvs → scm}/cvs-test.log +0 -0
- data/test/rscm/{cvs → scm}/cvs_log_parser_test.rb +12 -13
- data/test/rscm/{cvs → scm}/cvs_test.rb +7 -7
- data/test/rscm/{darcs → scm}/darcs_test.rb +1 -1
- data/test/rscm/{monotone → scm}/keys +0 -0
- data/test/rscm/scm/monotone_log_parser_test.rb +109 -0
- data/test/rscm/{monotone → scm}/monotone_test.rb +1 -1
- data/test/rscm/{mooky → scm}/mooky_test.rb +1 -1
- data/test/rscm/{perforce → scm}/perforce_test.rb +1 -1
- data/test/rscm/{starteam/starteam_test.rb → scm/star_team.rb} +1 -1
- data/test/rscm/{svn/svn_log_parser_test.rb → scm/subversion_log_parser_test.rb} +25 -10
- data/test/rscm/{svn/svn_test.rb → scm/subversion_test.rb} +4 -5
- data/test/rscm/{svn/cargo-svn.log → scm/svn-cargo.log} +0 -0
- data/test/rscm/scm/svn-growl.log +875 -0
- data/test/rscm/scm/svn-growl2.log +30 -0
- data/test/rscm/{svn/proxytoys-svn.log → scm/svn-proxytoys.log} +0 -0
- metadata +35 -44
- data/lib/rscm/attr_attr.rb +0 -36
- data/lib/rscm/monotone/monotone.rb +0 -107
- data/lib/rscm/mooky/mooky.rb +0 -13
- data/test/actual +0 -3
- data/test/expected +0 -3
- data/test/rscm/attr_attr_test.rb +0 -32
@@ -1,12 +1,15 @@
|
|
1
|
-
require 'rscm/abstract_scm'
|
2
1
|
require 'tempfile'
|
3
|
-
require 'rscm/path_converter'
|
4
2
|
require 'fileutils'
|
3
|
+
require 'rscm'
|
5
4
|
|
6
5
|
module RSCM
|
7
6
|
class Darcs < AbstractSCM
|
7
|
+
register self
|
8
8
|
|
9
|
-
|
9
|
+
ann :description => "Directory"
|
10
|
+
attr_accessor :dir
|
11
|
+
|
12
|
+
def initialize(dir=".")
|
10
13
|
@dir = File.expand_path(dir)
|
11
14
|
end
|
12
15
|
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'rscm'
|
3
|
+
|
4
|
+
module RSCM
|
5
|
+
class Monotone < AbstractSCM
|
6
|
+
register self
|
7
|
+
|
8
|
+
ann :description => "Database file"
|
9
|
+
attr_accessor :db_file
|
10
|
+
|
11
|
+
ann :description => "Branch"
|
12
|
+
attr_accessor :branch
|
13
|
+
|
14
|
+
ann :description => "Key"
|
15
|
+
attr_accessor :key
|
16
|
+
|
17
|
+
ann :description => "Passphrase"
|
18
|
+
attr_accessor :passphrase
|
19
|
+
|
20
|
+
ann :description => "Keys file"
|
21
|
+
attr_accessor :keys_file
|
22
|
+
|
23
|
+
def initialize(db_file="MT.db", branch="", key="", passphrase="", keys_file="")
|
24
|
+
@db_file = File.expand_path(db_file)
|
25
|
+
@branch = branch
|
26
|
+
@key = key
|
27
|
+
@passphrase = passphrase
|
28
|
+
@keys_file = keys_file
|
29
|
+
end
|
30
|
+
|
31
|
+
def name
|
32
|
+
"Monotone"
|
33
|
+
end
|
34
|
+
|
35
|
+
def create
|
36
|
+
FileUtils.mkdir_p(File.dirname(@db_file))
|
37
|
+
monotone("db init")
|
38
|
+
monotone("read") do |io|
|
39
|
+
io.write(File.open(@keys_file).read)
|
40
|
+
io.close_write
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def transactional?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def import(dir, message)
|
49
|
+
dir = File.expand_path(dir)
|
50
|
+
|
51
|
+
# post 0.17, this can be "cd dir && cmd add ."
|
52
|
+
|
53
|
+
files = Dir["#{dir}/*"]
|
54
|
+
relative_paths_to_add = to_relative(dir, files)
|
55
|
+
|
56
|
+
with_working_dir(dir) do
|
57
|
+
monotone("add #{relative_paths_to_add.join(' ')}")
|
58
|
+
monotone("commit '#{message}'", @branch, @key) do |io|
|
59
|
+
io.puts(@passphrase)
|
60
|
+
io.close_write
|
61
|
+
io.read
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def checked_out?(checkout_dir)
|
67
|
+
File.exists?("#{checkout_dir}/MT")
|
68
|
+
end
|
69
|
+
|
70
|
+
def uptodate?(checkout_dir, from_identifier)
|
71
|
+
if (!checked_out?(checkout_dir))
|
72
|
+
false
|
73
|
+
else
|
74
|
+
lr = local_revision(checkout_dir)
|
75
|
+
hr = head_revision(checkout_dir)
|
76
|
+
lr == hr
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def local_revision(checkout_dir)
|
81
|
+
local_revision = nil
|
82
|
+
rev_file = File.expand_path("#{checkout_dir}/MT/revision")
|
83
|
+
local_revision = File.open(rev_file).read.strip
|
84
|
+
local_revision
|
85
|
+
end
|
86
|
+
|
87
|
+
def head_revision(checkout_dir)
|
88
|
+
# FIXME: this will grab last head if heads are not merged.
|
89
|
+
head_revision = nil
|
90
|
+
monotone("heads", @branch) do |stdout|
|
91
|
+
stdout.each_line do |line|
|
92
|
+
next if (line =~ /^monotone:/)
|
93
|
+
head_revision = line.split(" ")[0]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
head_revision
|
97
|
+
end
|
98
|
+
|
99
|
+
def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
|
100
|
+
from_identifier = Time.epoch if from_identifier.nil?
|
101
|
+
to_identifier = Time.infinity if to_identifier.nil?
|
102
|
+
with_working_dir(checkout_dir) do
|
103
|
+
monotone("log", @branch, @key) do |stdout|
|
104
|
+
MonotoneLogParser.new.parse_changesets(stdout, from_identifier, to_identifier)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def commit(checkout_dir, message)
|
110
|
+
with_working_dir(checkout_dir) do
|
111
|
+
monotone("commit '#{message}'", @branch, @key) do |io|
|
112
|
+
io.puts(@passphrase)
|
113
|
+
io.close_write
|
114
|
+
io.read
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
|
121
|
+
# Checks out silently. Called by superclass' checkout.
|
122
|
+
def checkout_silent(checkout_dir, to_identifier)
|
123
|
+
checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
|
124
|
+
if checked_out?(checkout_dir)
|
125
|
+
with_working_dir(checkout_dir) do
|
126
|
+
monotone("update")
|
127
|
+
end
|
128
|
+
else
|
129
|
+
monotone("checkout #{checkout_dir}", @branch, @key) do |stdout|
|
130
|
+
stdout.each_line do |line|
|
131
|
+
# TODO: checkout prints nothing to stdout - may be fixed in a future monotone.
|
132
|
+
# When/if it happens we may want to do a kosher implementation of checkout
|
133
|
+
# to get yields as checkouts happen.
|
134
|
+
yield line if block_given?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Administrative files that should be ignored when counting files.
|
141
|
+
def ignore_paths
|
142
|
+
return [/MT/, /\.mt-attrs/]
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def monotone(monotone_cmd, branch=nil, key=nil)
|
148
|
+
branch_opt = branch ? "--branch=\"#{branch}\"" : ""
|
149
|
+
key_opt = key ? "--key=\"#{key}\"" : ""
|
150
|
+
cmd = "monotone --db=\"#{@db_file}\" #{branch_opt} #{key_opt} #{monotone_cmd}"
|
151
|
+
safer_popen(cmd, "r+") do |io|
|
152
|
+
if(block_given?)
|
153
|
+
return(yield(io))
|
154
|
+
else
|
155
|
+
# just read stdout so we can exit
|
156
|
+
io.read
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rscm'
|
2
|
+
require 'time'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module RSCM
|
6
|
+
|
7
|
+
class MonotoneLogParser
|
8
|
+
|
9
|
+
def parse_changesets(io, from_identifier=Time.epoch, to_identifier=Time.infinity)
|
10
|
+
# skip first separator
|
11
|
+
io.readline
|
12
|
+
|
13
|
+
changesets = ChangeSets.new
|
14
|
+
changeset_string = ""
|
15
|
+
|
16
|
+
# hash of path => [array of revisions]
|
17
|
+
path_revisions = {}
|
18
|
+
io.each_line do |line|
|
19
|
+
if(line =~ /-----------------------------------------------------------------/)
|
20
|
+
changeset = parse_changeset(StringIO.new(changeset_string), path_revisions)
|
21
|
+
changesets.add(changeset)
|
22
|
+
changeset_string = ""
|
23
|
+
else
|
24
|
+
changeset_string << line
|
25
|
+
end
|
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]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
changesets
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_changeset(changeset_io, path_revisions)
|
43
|
+
changeset = ChangeSet.new
|
44
|
+
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
|
+
$1[0..3].to_i,
|
53
|
+
$1[5..6].to_i,
|
54
|
+
$1[8..9].to_i,
|
55
|
+
$1[11..12].to_i,
|
56
|
+
$1[14..15].to_i,
|
57
|
+
$1[17..18].to_i
|
58
|
+
)
|
59
|
+
elsif(line =~ /^ChangeLog:$/ && changeset.message.nil?)
|
60
|
+
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:$/)
|
66
|
+
state = :added
|
67
|
+
elsif(state == :added)
|
68
|
+
add_changes(changeset, line, Change::ADDED, path_revisions)
|
69
|
+
elsif(line =~ /^Modified files:$/)
|
70
|
+
state = :modified
|
71
|
+
elsif(state == :modified)
|
72
|
+
add_changes(changeset, line, Change::MODIFIED, path_revisions)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
changeset.message.chomp!
|
76
|
+
changeset
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def add_changes(changeset, line, state, path_revisions)
|
82
|
+
paths = line.split(" ")
|
83
|
+
paths.each do |path|
|
84
|
+
changeset << Change.new(path, state, changeset.developer, nil, changeset.revision, changeset.time)
|
85
|
+
|
86
|
+
# now record path revisions so we can keep track of previous rev for each path
|
87
|
+
# doesn't work for moved files, and have no idea how to make it work either.
|
88
|
+
path_revisions[path] ||= []
|
89
|
+
path_revisions[path] << changeset.revision
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rscm/abstract_scm'
|
2
|
+
|
3
|
+
module RSCM
|
4
|
+
class Mooky < AbstractSCM
|
5
|
+
register self
|
6
|
+
|
7
|
+
ann :description => "The Foo", :tip => "Foo is nonsense"
|
8
|
+
attr_accessor :foo
|
9
|
+
|
10
|
+
ann :description => "Le Bar", :tip => "Bar toi!"
|
11
|
+
attr_accessor :bar
|
12
|
+
|
13
|
+
def initialize(foo="", bar="chocolate bar")
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
"Mooky"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -15,11 +15,14 @@ module RSCM
|
|
15
15
|
# You need the p4/p4d executable on the PATH in order for it to work.
|
16
16
|
#
|
17
17
|
class Perforce < AbstractSCM
|
18
|
+
register self
|
19
|
+
|
18
20
|
include FileUtils
|
19
21
|
|
22
|
+
ann :description => "Depot path", :tip => "The path to the Perforce depot"
|
20
23
|
attr_accessor :depotpath
|
21
24
|
|
22
|
-
def initialize(repository_root_dir =
|
25
|
+
def initialize(repository_root_dir = "")
|
23
26
|
@clients = {}
|
24
27
|
@depotpath = repository_root_dir
|
25
28
|
end
|
@@ -55,8 +58,8 @@ module RSCM
|
|
55
58
|
client(checkout_dir).submit(message, &proc)
|
56
59
|
end
|
57
60
|
|
58
|
-
def changesets(checkout_dir, from_identifier, to_identifier
|
59
|
-
client(checkout_dir).changesets(from_identifier, to_identifier
|
61
|
+
def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
|
62
|
+
client(checkout_dir).changesets(from_identifier, to_identifier)
|
60
63
|
end
|
61
64
|
|
62
65
|
def uptodate?(checkout_dir, from_identifier)
|
@@ -251,7 +254,7 @@ module RSCM
|
|
251
254
|
p4("sync -n").empty?
|
252
255
|
end
|
253
256
|
|
254
|
-
def changesets(from_identifier, to_identifier
|
257
|
+
def changesets(from_identifier, to_identifier)
|
255
258
|
changesets = changelists(from_identifier, to_identifier).collect {|changelist| to_changeset(changelist)}
|
256
259
|
ChangeSets.new(changesets)
|
257
260
|
end
|
@@ -18,10 +18,30 @@ module RSCM
|
|
18
18
|
# * Apache Ant (http://ant.apache.org/)
|
19
19
|
#
|
20
20
|
class StarTeam < AbstractSCM
|
21
|
+
register self
|
21
22
|
|
22
|
-
|
23
|
+
ann :description => "User name"
|
24
|
+
attr_accessor :user_name
|
23
25
|
|
24
|
-
|
26
|
+
ann :description => "Password"
|
27
|
+
attr_accessor :password
|
28
|
+
|
29
|
+
ann :description => "Server name"
|
30
|
+
attr_accessor :server_name
|
31
|
+
|
32
|
+
ann :description => "Server port"
|
33
|
+
attr_accessor :server_port
|
34
|
+
|
35
|
+
ann :description => "Project name"
|
36
|
+
attr_accessor :project_name
|
37
|
+
|
38
|
+
ann :description => "View name"
|
39
|
+
attr_accessor :view_name
|
40
|
+
|
41
|
+
ann :description => "Folder name"
|
42
|
+
attr_accessor :folder_name
|
43
|
+
|
44
|
+
def initialize(user_name="", password="", server_name="", server_port="", project_name="", view_name="", folder_name="")
|
25
45
|
@user_name, @password, @server_name, @server_port, @project_name, @view_name, @folder_name = user_name, password, server_name, server_port, project_name, view_name, folder_name
|
26
46
|
end
|
27
47
|
|
@@ -29,7 +49,7 @@ module RSCM
|
|
29
49
|
"StarTeam"
|
30
50
|
end
|
31
51
|
|
32
|
-
def changesets(checkout_dir, from_identifier=Time.epoch, to_identifier=Time.infinity,
|
52
|
+
def changesets(checkout_dir, from_identifier=Time.epoch, to_identifier=Time.infinity, &proc)
|
33
53
|
# just assuming it is a Time for now, may support labels later.
|
34
54
|
# the java class really wants rfc822 and not rfc2822, but this works ok anyway.
|
35
55
|
from = from_identifier.to_rfc2822
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rscm/abstract_scm'
|
2
2
|
require 'rscm/path_converter'
|
3
3
|
require 'rscm/line_editor'
|
4
|
-
require 'rscm/
|
4
|
+
require 'rscm/scm/subversion_log_parser'
|
5
5
|
|
6
6
|
module RSCM
|
7
7
|
|
@@ -10,14 +10,21 @@ 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
|
13
|
+
class Subversion < AbstractSCM
|
14
|
+
register self
|
15
|
+
|
14
16
|
include FileUtils
|
15
17
|
include PathConverter
|
16
18
|
|
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"
|
17
21
|
attr_accessor :url
|
22
|
+
|
23
|
+
ann :description => "Path"
|
24
|
+
ann :tip => "This is 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."
|
18
25
|
attr_accessor :path
|
19
26
|
|
20
|
-
def initialize(url=
|
27
|
+
def initialize(url="", path="trunk")
|
21
28
|
@url, @path = url, path
|
22
29
|
end
|
23
30
|
|
@@ -98,7 +105,7 @@ module RSCM
|
|
98
105
|
cmd = "svn log #{repourl} -r HEAD"
|
99
106
|
with_working_dir(checkout_dir) do
|
100
107
|
safer_popen(cmd) do |stdout|
|
101
|
-
parser =
|
108
|
+
parser = SubversionLogParser.new(stdout, path, checkout_dir)
|
102
109
|
changesets = parser.parse_changesets
|
103
110
|
changesets[0].revision.to_i
|
104
111
|
end
|
@@ -117,7 +124,7 @@ module RSCM
|
|
117
124
|
|
118
125
|
def diff(checkout_dir, change, &block)
|
119
126
|
with_working_dir(checkout_dir) do
|
120
|
-
cmd = "svn diff -r #{change.previous_revision}:#{change.revision} #{url}/#{change.path}"
|
127
|
+
cmd = "svn diff -r #{change.previous_revision}:#{change.revision} \"#{url}/#{change.path}\""
|
121
128
|
safer_popen(cmd) do |io|
|
122
129
|
return(yield(io))
|
123
130
|
end
|
@@ -179,18 +186,18 @@ module RSCM
|
|
179
186
|
svn(dir, import_cmd)
|
180
187
|
end
|
181
188
|
|
182
|
-
def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity
|
189
|
+
def changesets(checkout_dir, from_identifier, to_identifier=Time.infinity)
|
183
190
|
# Return empty changeset if the requested revision doesn't exist yet.
|
184
191
|
return ChangeSets.new if(from_identifier.is_a?(Integer) && head_revision(checkout_dir) < from_identifier)
|
185
192
|
|
186
193
|
checkout_dir = PathConverter.filepath_to_nativepath(checkout_dir, false)
|
187
194
|
changesets = nil
|
188
|
-
command = "svn #{changes_command(from_identifier, to_identifier
|
195
|
+
command = "svn #{changes_command(from_identifier, to_identifier)}"
|
189
196
|
yield command if block_given?
|
190
197
|
|
191
198
|
with_working_dir(checkout_dir) do
|
192
199
|
safer_popen(command) do |stdout|
|
193
|
-
parser =
|
200
|
+
parser = SubversionLogParser.new(stdout, path, checkout_dir)
|
194
201
|
changesets = parser.parse_changesets
|
195
202
|
end
|
196
203
|
end
|
@@ -274,7 +281,7 @@ module RSCM
|
|
274
281
|
"update #{revision_option(nil,to_identifier)}"
|
275
282
|
end
|
276
283
|
|
277
|
-
def changes_command(from_identifier, to_identifier
|
284
|
+
def changes_command(from_identifier, to_identifier)
|
278
285
|
# http://svnbook.red-bean.com/svnbook-1.1/svn-book.html#svn-ch-3-sect-3.3
|
279
286
|
# file_list = files.join('\n')
|
280
287
|
# WEIRD cygwin bug garbles this!?!?!?!
|
@@ -312,7 +319,7 @@ module RSCM
|
|
312
319
|
|
313
320
|
def svndate(time)
|
314
321
|
return nil unless time
|
315
|
-
time.utc.strftime("\"
|
322
|
+
time.utc.strftime("{\"%Y-%m-%d %H:%M:%S\"}")
|
316
323
|
end
|
317
324
|
|
318
325
|
def commit_command(message)
|