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
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rscm/
|
1
|
+
require 'rscm/revision'
|
2
2
|
require 'rscm/abstract_log_parser'
|
3
3
|
|
4
4
|
require 'ftools'
|
@@ -17,19 +17,19 @@ module RSCM
|
|
17
17
|
@log = ""
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def parse_revisions
|
21
|
+
revisions = Revisions.new
|
22
22
|
while(log_entry = next_log_entry)
|
23
23
|
@log<<log_entry
|
24
24
|
@log<<""
|
25
25
|
begin
|
26
|
-
|
26
|
+
parse_files(log_entry, revisions)
|
27
27
|
rescue Exception => e
|
28
28
|
$stderr.puts("could not parse log entry: #{log_entry}\ndue to: #{e.message}\n\t")
|
29
29
|
$stderr.puts(e.backtrace.join("\n\t"))
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
revisions.sort!
|
33
33
|
end
|
34
34
|
|
35
35
|
def next_log_entry
|
@@ -48,20 +48,20 @@ module RSCM
|
|
48
48
|
entries
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
51
|
+
def parse_files(log_entry, revisions)
|
52
52
|
entries = split_entries(log_entry)
|
53
53
|
|
54
54
|
entries[1..entries.length].each do |entry|
|
55
|
-
|
56
|
-
next if
|
57
|
-
|
55
|
+
file = parse_file(entry)
|
56
|
+
next if file.nil?
|
57
|
+
file.path = parse_path(entries[0])
|
58
58
|
|
59
|
-
|
59
|
+
file.status = RevisionFile::ADDED if file.native_revision_identifier =~ /1\.1$/
|
60
60
|
|
61
|
-
|
62
|
-
# CVS doesn't have revision for
|
61
|
+
revision = revisions.add(file)
|
62
|
+
# CVS doesn't have revision for revisions, use
|
63
63
|
# Fisheye-style revision
|
64
|
-
#
|
64
|
+
# revision.native_revision_identifier = "MAIN:#{file.developer}:#{file.time.utc.strftime('%Y%m%d%H%M%S')}" if revision
|
65
65
|
end
|
66
66
|
nil
|
67
67
|
end
|
@@ -82,39 +82,39 @@ module RSCM
|
|
82
82
|
convert_all_slashes_to_forward_slashes(file).gsub(/^#{cvspath}\/#{cvsmodule}\//, "")
|
83
83
|
end
|
84
84
|
|
85
|
-
def
|
86
|
-
raise "can't parse: #{
|
85
|
+
def parse_file(file_entry)
|
86
|
+
raise "can't parse: #{file_entry}" if file_entry =~ REVISION_SEPARATOR
|
87
87
|
|
88
|
-
|
89
|
-
|
88
|
+
file_entry_lines = file_entry.split(/\r?\n/)
|
89
|
+
file = RevisionFile.new
|
90
90
|
|
91
|
-
|
91
|
+
file.native_revision_identifier = extract_match(file_entry_lines[0], /revision (.*)$/)
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
file.previous_native_revision_identifier = determine_previous_native_revision_identifier(file.native_revision_identifier)
|
94
|
+
file.time = parse_cvs_time(extract_required_match(file_entry_lines[1], /date: (.*?)(;|$)/))
|
95
|
+
file.developer = extract_match(file_entry_lines[1], /author: (.*?);/)
|
96
96
|
|
97
|
-
state = extract_match(
|
98
|
-
|
97
|
+
state = extract_match(file_entry_lines[1], /state: (.*?);/)
|
98
|
+
file.status = STATES[state]
|
99
99
|
|
100
100
|
message_start = 2
|
101
101
|
branches = nil
|
102
|
-
if(
|
102
|
+
if(file_entry_lines[2] =~ /^branches:\s+(.*);/)
|
103
103
|
message_start = 3
|
104
104
|
branches = $1
|
105
105
|
end
|
106
106
|
|
107
|
-
|
107
|
+
file.message = file_entry_lines[message_start..-1].join("\n")
|
108
108
|
|
109
109
|
# Ignore the initial revision from import - we will have two of them
|
110
|
-
if(
|
110
|
+
if(file.message == "Initial revision" && branches == "1.1.1")
|
111
111
|
return nil
|
112
112
|
end
|
113
113
|
|
114
|
-
|
114
|
+
file
|
115
115
|
end
|
116
116
|
|
117
|
-
def
|
117
|
+
def determine_previous_native_revision_identifier(revision)
|
118
118
|
if revision =~ /(.*)\.(.*)/
|
119
119
|
big_version_number = $1
|
120
120
|
small_version_number = $2.to_i
|
@@ -154,7 +154,7 @@ module RSCM
|
|
154
154
|
# The state field is "Exp" both for added and modified files. retards!
|
155
155
|
# We need some additional logic to figure out whether it is added or not.
|
156
156
|
# Maybe look at the revision. (1.1 means new I think. - deal with it later)
|
157
|
-
STATES = {"dead" =>
|
157
|
+
STATES = {"dead" => RevisionFile::DELETED, "Exp" => RevisionFile::MODIFIED} unless defined? STATES
|
158
158
|
|
159
159
|
end
|
160
160
|
|
data/lib/rscm/scm/darcs.rb
CHANGED
@@ -3,8 +3,8 @@ require 'fileutils'
|
|
3
3
|
require 'rscm'
|
4
4
|
|
5
5
|
module RSCM
|
6
|
-
class Darcs <
|
7
|
-
register self
|
6
|
+
class Darcs < Base
|
7
|
+
#register self
|
8
8
|
|
9
9
|
ann :description => "Directory"
|
10
10
|
attr_accessor :dir
|
@@ -17,56 +17,104 @@ module RSCM
|
|
17
17
|
"Darcs"
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def can_create_central?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_central
|
21
25
|
with_working_dir(@dir) do
|
22
|
-
|
23
|
-
stdout.each_line do |line|
|
24
|
-
yield line if block_given?
|
25
|
-
end
|
26
|
-
end
|
26
|
+
darcs("initialize")
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def import_central(dir, message)
|
31
31
|
ENV["EMAIL"] = "dcontrol@codehaus.org"
|
32
32
|
FileUtils.cp_r(Dir.glob("#{dir}/*"), @dir)
|
33
33
|
with_working_dir(@dir) do
|
34
|
-
|
35
|
-
|
36
|
-
puts cmd
|
37
|
-
IO.popen(cmd) do |stdout|
|
38
|
-
stdout.each_line do |line|
|
39
|
-
yield line if block_given?
|
40
|
-
end
|
41
|
-
end
|
42
|
-
puts $?
|
34
|
+
darcs("add --recursive .")
|
35
|
+
|
43
36
|
logfile = Tempfile.new("darcs_logfile")
|
44
|
-
logfile.print(
|
37
|
+
logfile.print("something nice\n")
|
38
|
+
logfile.print(message + "\n")
|
45
39
|
logfile.close
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
darcs("record --all --logfile #{PathConverter.filepath_to_nativepath(logfile.path, false)}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def commit(message)
|
46
|
+
logfile = Tempfile.new("darcs_logfile")
|
47
|
+
logfile.print("something nice\n")
|
48
|
+
logfile.print(message + "\n")
|
49
|
+
logfile.close
|
50
|
+
|
51
|
+
with_working_dir(@checkout_dir) do
|
52
|
+
darcs("record --all --logfile #{PathConverter.filepath_to_nativepath(logfile.path, false)}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def add(relative_filename)
|
57
|
+
with_working_dir(@checkout_dir) do
|
58
|
+
darcs("add #{relative_filename}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def checked_out?
|
63
|
+
File.exists?("#{@checkout_dir}/_darcs")
|
64
|
+
end
|
65
|
+
|
66
|
+
def uptodate?(from_identifier)
|
67
|
+
if (!checked_out?(@checkout_dir))
|
68
|
+
false
|
69
|
+
else
|
70
|
+
with_working_dir(@checkout_dir) do
|
71
|
+
darcs("pull --dry-run #{@dir}") do |io|
|
72
|
+
io.each_line do |line|
|
73
|
+
if (line =~ /No remote changes to pull in!/)
|
74
|
+
true
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
52
79
|
end
|
53
80
|
end
|
54
|
-
puts $?
|
55
81
|
end
|
56
82
|
end
|
57
83
|
|
58
|
-
def
|
84
|
+
def revisions(from_identifier, to_identifier=Time.infinity)
|
85
|
+
from_identifier = Time.epoch if from_identifier.nil?
|
86
|
+
to_identifier = Time.infinity if to_identifier.nil?
|
87
|
+
with_working_dir(@checkout_dir) do
|
88
|
+
darcs("changes --summary --xml-output") do |stdout|
|
89
|
+
DarcsLogParser.new.parse_revisions(stdout, from_identifier, to_identifier)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def checkout_silent(to_identifier) # :yield: file
|
59
97
|
with_working_dir(File.dirname(checkout_dir)) do
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
98
|
+
darcs("get --repo-name #{File.basename(checkout_dir)} #{@dir}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def ignore_paths
|
103
|
+
return [/_darcs/]
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def darcs(darcs_cmd)
|
109
|
+
cmd = "darcs #{darcs_cmd}"
|
110
|
+
|
111
|
+
Better.popen(cmd, "r+") do |io|
|
112
|
+
if(block_given?)
|
113
|
+
return(yield(io))
|
114
|
+
else
|
115
|
+
io.read
|
67
116
|
end
|
68
117
|
end
|
69
|
-
puts $?
|
70
118
|
end
|
71
119
|
end
|
72
120
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rscm'
|
2
|
+
require 'time'
|
3
|
+
require 'stringio'
|
4
|
+
require 'rexml/document'
|
5
|
+
|
6
|
+
module RSCM
|
7
|
+
class DarcsLogParser
|
8
|
+
def parse_revisions(io, from_identifier=Time.epoch, to_identifier=Time.infinity)
|
9
|
+
revisions = Revisions.new
|
10
|
+
|
11
|
+
doc = REXML::Document.new(io)
|
12
|
+
|
13
|
+
path_revisions = {}
|
14
|
+
doc.elements.each("//patch") do |element|
|
15
|
+
revision = parse_revision(element.to_s, path_revisions)
|
16
|
+
if ((from_identifier <= revision.time) && (revision.time <= to_identifier))
|
17
|
+
revisions.add(revision)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
revisions.each do |revision|
|
22
|
+
revision.each do |change|
|
23
|
+
current_index = path_revisions[change.path].index(change.native_revision_identifier)
|
24
|
+
change.previous_native_revision_identifier = path_revisions[change.path][current_index + 1]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
revisions
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_revision(revision_io, path_revisions)
|
32
|
+
revision = Revision.new
|
33
|
+
|
34
|
+
doc = REXML::Document.new(revision_io)
|
35
|
+
|
36
|
+
doc.elements.each("patch") do |element|
|
37
|
+
revision.identifier = element.attributes['hash']
|
38
|
+
revision.developer = element.attributes['author']
|
39
|
+
revision.time = Time.parse(element.attributes['local_date'])
|
40
|
+
revision.message = element.elements["comment"].text
|
41
|
+
revision.message.lstrip!
|
42
|
+
revision.message.rstrip!
|
43
|
+
|
44
|
+
element.elements["summary"].elements.each("add_file") do |file|
|
45
|
+
add_changes(revision, file.text.strip, RevisionFile::ADDED, path_revisions)
|
46
|
+
end
|
47
|
+
element.elements["summary"].elements.each("modify_file") do |file|
|
48
|
+
add_changes(revision, file.text.strip, RevisionFile::MODIFIED, path_revisions)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
revision
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def add_changes(revision, path, state, path_revisions)
|
58
|
+
revision << RevisionFile.new(path, state, revision.developer, nil, revision.identifier, revision.time)
|
59
|
+
|
60
|
+
path_revisions[path] ||= []
|
61
|
+
path_revisions[path] << revision.identifier
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/rscm/scm/monotone.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
require 'rscm/line_editor'
|
1
2
|
require 'fileutils'
|
2
3
|
require 'rscm'
|
3
4
|
|
4
5
|
module RSCM
|
5
|
-
class Monotone <
|
6
|
+
class Monotone < Base
|
6
7
|
register self
|
7
8
|
|
8
9
|
ann :description => "Database file"
|
@@ -20,101 +21,130 @@ module RSCM
|
|
20
21
|
ann :description => "Keys file"
|
21
22
|
attr_accessor :keys_file
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
ann :description => "Server"
|
25
|
+
attr_accessor :server
|
26
|
+
|
27
|
+
def initialize(branch=nil, key=nil, passphrase=nil, keys_file=nil, server=nil, central_checkout_dir=nil)
|
25
28
|
@branch = branch
|
26
29
|
@key = key
|
27
30
|
@passphrase = passphrase
|
28
31
|
@keys_file = keys_file
|
32
|
+
@server = server
|
33
|
+
@central_checkout_dir = File.expand_path(central_checkout_dir) unless central_checkout_dir.nil?
|
29
34
|
end
|
30
35
|
|
31
36
|
def name
|
32
37
|
"Monotone"
|
33
38
|
end
|
34
39
|
|
35
|
-
def add(
|
36
|
-
|
37
|
-
|
40
|
+
def add(relative_filename)
|
41
|
+
db = db(@checkout_dir)
|
42
|
+
with_working_dir(@checkout_dir) do
|
43
|
+
monotone("add #{relative_filename}", db)
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
41
|
-
def
|
42
|
-
|
43
|
-
monotone("db init")
|
44
|
-
monotone("read") do |io|
|
45
|
-
io.write(File.open(@keys_file).read)
|
46
|
-
io.close_write
|
47
|
-
end
|
47
|
+
def can_create_central?
|
48
|
+
@server == "localhost" && !@central_checkout_dir.nil?
|
48
49
|
end
|
49
|
-
|
50
|
-
def
|
51
|
-
|
50
|
+
|
51
|
+
def central_exists?
|
52
|
+
@central_checkout_dir && @serve_pid
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
def create_central
|
56
|
+
init(@central_checkout_dir)
|
57
|
+
# create empty working copy
|
58
|
+
dir = PathConverter.filepath_to_nativepath(@central_checkout_dir, false)
|
59
|
+
# set up a working copy
|
60
|
+
monotone("setup #{dir}")
|
61
|
+
start_serve
|
62
|
+
end
|
63
|
+
|
64
|
+
def start_serve
|
65
|
+
mode = File::CREAT|File::WRONLY
|
66
|
+
if File.exist?(rcfile)
|
67
|
+
mode = File::APPEND|File::WRONLY
|
68
|
+
end
|
58
69
|
|
59
|
-
|
60
|
-
|
70
|
+
begin
|
71
|
+
File.open(rcfile, mode) do |file|
|
72
|
+
file.puts("function get_netsync_anonymous_read_permitted(collection)")
|
73
|
+
file.puts(" return true")
|
74
|
+
file.puts("end")
|
75
|
+
end
|
76
|
+
rescue => e
|
77
|
+
puts e.message
|
78
|
+
puts e.backtrace.join("\n")
|
79
|
+
raise "Didn't have permission to write to #{rcfile}."
|
80
|
+
end
|
61
81
|
|
62
|
-
|
63
|
-
|
64
|
-
monotone("
|
82
|
+
@serve_pid = fork do
|
83
|
+
#Signal.trap("HUP") { puts "Monotone server shutting down..."; exit }
|
84
|
+
monotone("serve --rcfile=\"#{rcfile}\" #{@server} #{@branch}", db(@central_checkout_dir)) do |io|
|
85
|
+
puts "PASSPHRASE: #{@passphrase}"
|
65
86
|
io.puts(@passphrase)
|
66
87
|
io.close_write
|
67
|
-
io.read
|
68
88
|
end
|
69
89
|
end
|
90
|
+
Process.detach(@serve_pid)
|
70
91
|
end
|
71
|
-
|
72
|
-
def
|
73
|
-
|
92
|
+
|
93
|
+
def stop_serve
|
94
|
+
Process.kill("HUP", @serve_pid) if @serve_pid
|
95
|
+
Process.waitpid2(@serve_pid) if @serve_pid
|
96
|
+
@serve_pid = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def destroy_central
|
100
|
+
stop_serve
|
101
|
+
FileUtils.rm_rf(@central_checkout_dir) if File.exist?(@central_checkout_dir)
|
102
|
+
FileUtils.rm(db(@central_checkout_dir)) if File.exist?(db(@central_checkout_dir))
|
103
|
+
puts "Destroyed Monotone server"
|
104
|
+
end
|
105
|
+
|
106
|
+
def transactional?
|
107
|
+
true
|
74
108
|
end
|
75
109
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
hr = head_revision(checkout_dir)
|
82
|
-
lr == hr
|
110
|
+
def import_central(dir, message)
|
111
|
+
cp_r(Dir["#{dir}/*"], @central_checkout_dir)
|
112
|
+
with_working_dir(@central_checkout_dir) do
|
113
|
+
monotone("add .")
|
114
|
+
commit_in_dir(message, @central_checkout_dir)
|
83
115
|
end
|
84
116
|
end
|
85
117
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
local_revision = File.open(rev_file).read.strip
|
90
|
-
local_revision
|
118
|
+
def checked_out?
|
119
|
+
mt = File.expand_path("#{@checkout_dir}/MT")
|
120
|
+
File.exists?(mt)
|
91
121
|
end
|
92
|
-
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
122
|
+
|
123
|
+
def uptodate?(identifier=nil)
|
124
|
+
if (!checked_out?)
|
125
|
+
false
|
126
|
+
else
|
127
|
+
pull
|
128
|
+
|
129
|
+
rev = identifier ? identifier : head_revision
|
130
|
+
local_revision == rev
|
101
131
|
end
|
102
|
-
head_revision
|
103
132
|
end
|
104
133
|
|
105
|
-
def
|
106
|
-
|
134
|
+
def revisions(from_identifier, to_identifier=Time.infinity)
|
135
|
+
checkout(to_identifier)
|
107
136
|
to_identifier = Time.infinity if to_identifier.nil?
|
108
137
|
with_working_dir(checkout_dir) do
|
109
|
-
monotone("log"
|
110
|
-
MonotoneLogParser.new.
|
138
|
+
monotone("log") do |stdout|
|
139
|
+
MonotoneLogParser.new.parse_revisions(stdout, from_identifier, to_identifier)
|
111
140
|
end
|
112
141
|
end
|
113
142
|
end
|
114
143
|
|
115
|
-
def commit(
|
116
|
-
|
117
|
-
|
144
|
+
def commit(message)
|
145
|
+
commit_in_dir(message, @checkout_dir)
|
146
|
+
with_working_dir(@checkout_dir) do
|
147
|
+
monotone("push #{@server} #{@branch}") do |io|
|
118
148
|
io.puts(@passphrase)
|
119
149
|
io.close_write
|
120
150
|
io.read
|
@@ -122,24 +152,54 @@ module RSCM
|
|
122
152
|
end
|
123
153
|
end
|
124
154
|
|
155
|
+
# http://www.venge.net/monotone/monotone.html#Hook-Reference
|
156
|
+
def install_trigger(trigger_command, install_dir)
|
157
|
+
stop_serve
|
158
|
+
if (WINDOWS)
|
159
|
+
install_win_trigger(trigger_comand, install_dir)
|
160
|
+
else
|
161
|
+
install_unix_trigger(trigger_command, install_dir)
|
162
|
+
end
|
163
|
+
start_serve
|
164
|
+
end
|
165
|
+
|
166
|
+
def trigger_installed?(trigger_command, install_dir)
|
167
|
+
File.exist?(rcfile)
|
168
|
+
end
|
169
|
+
|
170
|
+
def uninstall_trigger(trigger_command, install_dir)
|
171
|
+
stop_serve
|
172
|
+
File.delete(rcfile)
|
173
|
+
start_serve
|
174
|
+
end
|
175
|
+
|
176
|
+
def diff(change, &block)
|
177
|
+
checkout(change.revision)
|
178
|
+
with_working_dir(@checkout_dir) do
|
179
|
+
monotone("diff --revision=#{change.previous_native_revision_identifier} #{change.path}") do |stdout|
|
180
|
+
yield stdout
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
125
185
|
protected
|
126
186
|
|
127
187
|
# Checks out silently. Called by superclass' checkout.
|
128
|
-
def checkout_silent(
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
188
|
+
def checkout_silent(to_identifier)
|
189
|
+
# raise "Monotone doesn't support checkout to time. Please use identifiers instead." if to_identifier.is_a?(Time)
|
190
|
+
db_file = db(@checkout_dir)
|
191
|
+
if(!File.exist?(db_file))
|
192
|
+
init(@checkout_dir)
|
193
|
+
end
|
194
|
+
|
195
|
+
pull
|
196
|
+
checked_out = checked_out?
|
197
|
+
|
198
|
+
with_working_dir(@checkout_dir) do
|
199
|
+
monotone("checkout .", db_file, @branch) unless checked_out
|
200
|
+
|
201
|
+
selector = expand_selector(to_identifier)
|
202
|
+
monotone("update #{selector}", db_file)
|
143
203
|
end
|
144
204
|
end
|
145
205
|
|
@@ -149,12 +209,119 @@ module RSCM
|
|
149
209
|
end
|
150
210
|
|
151
211
|
private
|
212
|
+
|
213
|
+
def commit_in_dir(message, dir)
|
214
|
+
db_file = db(dir)
|
215
|
+
with_working_dir(dir) do
|
216
|
+
monotone("commit --message='#{message}'", db_file, @branch, @key) do |io|
|
217
|
+
io.puts(@passphrase)
|
218
|
+
io.close_write
|
219
|
+
io.read
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def pull
|
225
|
+
db_file = db(@checkout_dir)
|
226
|
+
with_working_dir(@checkout_dir) do
|
227
|
+
# pull from the "central" server
|
228
|
+
if(@server)
|
229
|
+
monotone("pull #{@server} #{@branch}", db_file) do |io|
|
230
|
+
io.puts(@passphrase)
|
231
|
+
io.close_write
|
232
|
+
io.read
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def db(checkout_dir)
|
239
|
+
PathConverter.filepath_to_nativepath(checkout_dir + ".db", false)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Initialises a monotone database
|
243
|
+
#
|
244
|
+
def init(dir)
|
245
|
+
dir = File.expand_path(dir)
|
246
|
+
db_file = db(dir)
|
247
|
+
raise "Database #{db_file} already exists" if File.exist?(db_file)
|
248
|
+
FileUtils.mkdir_p(File.dirname(db_file))
|
249
|
+
# create database
|
250
|
+
monotone("db init", db_file)
|
251
|
+
# TODO: do a genkey
|
252
|
+
monotone("read", db_file) do |io|
|
253
|
+
io.write(File.open(@keys_file).read)
|
254
|
+
io.close_write
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def install_unix_trigger(trigger_command, install_dir)
|
259
|
+
mode = File::CREAT|File::WRONLY
|
260
|
+
if File.exist?(rcfile)
|
261
|
+
mode = File::APPEND|File::WRONLY
|
262
|
+
end
|
263
|
+
|
264
|
+
begin
|
265
|
+
File.open(rcfile, mode) do |file|
|
266
|
+
file.puts("function note_commit(new_id, certs)")
|
267
|
+
execstr = "\"" + trigger_command.split.join("\",\"") + "\""
|
268
|
+
file.puts(" execute(#{execstr})")
|
269
|
+
file.puts("end")
|
270
|
+
end
|
271
|
+
rescue => e
|
272
|
+
puts e.message
|
273
|
+
puts e.backtrace.join("\n")
|
274
|
+
raise "Didn't have permission to write to #{rcfile}."
|
275
|
+
end
|
276
|
+
|
277
|
+
# push to the "central" server
|
278
|
+
# monotone("push #{@server} #{@branch}", db(@central_checkout_dir))
|
279
|
+
end
|
280
|
+
|
281
|
+
def rcfile
|
282
|
+
"#{@central_checkout_dir}/MT/monotonerc"
|
283
|
+
end
|
284
|
+
|
285
|
+
def local_revision
|
286
|
+
local_revision = nil
|
287
|
+
rev_file = File.expand_path("#{checkout_dir}/MT/revision")
|
288
|
+
local_revision = File.open(rev_file).read.strip
|
289
|
+
local_revision
|
290
|
+
end
|
291
|
+
|
292
|
+
def head_revision
|
293
|
+
# FIXME: this will grab last head if heads are not merged.
|
294
|
+
head_revision = nil
|
295
|
+
monotone("heads", db(@checkout_dir), @branch) do |stdout|
|
296
|
+
stdout.each_line do |line|
|
297
|
+
next if (line =~ /^monotone:/)
|
298
|
+
head_revision = line.split(" ")[0]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
head_revision
|
302
|
+
end
|
303
|
+
|
304
|
+
# See http://www.venge.net/monotone/monotone.html#Selectors
|
305
|
+
# Also see docs for expand_selector in the same document
|
306
|
+
# Dates are formatted with strftime-style %F, which is of style 2005-28-02,
|
307
|
+
# which is very coarse grained. Date identifiers are therefore discouraged.
|
308
|
+
def expand_selector(identifier)
|
309
|
+
if(identifier.is_a?(Time))
|
310
|
+
# Won't work:
|
311
|
+
# "d:#{identifier.strftime('%Y-%m-%d')}"
|
312
|
+
""
|
313
|
+
else
|
314
|
+
"i:#{identifier}"
|
315
|
+
end
|
316
|
+
end
|
152
317
|
|
153
|
-
def monotone(monotone_cmd, branch=nil, key=nil)
|
318
|
+
def monotone(monotone_cmd, db_file=nil, branch=nil, key=nil)
|
319
|
+
db_opt = db_file ? "--db=\"#{db_file}\"" : ""
|
154
320
|
branch_opt = branch ? "--branch=\"#{branch}\"" : ""
|
155
321
|
key_opt = key ? "--key=\"#{key}\"" : ""
|
156
|
-
|
157
|
-
|
322
|
+
rcfile_opt = @rcfile ? "--rcfile=\"#{@rcfile}\"" : ""
|
323
|
+
cmd = "monotone #{db_opt} #{branch_opt} #{key_opt} #{rcfile_opt} #{monotone_cmd}"
|
324
|
+
Better.popen(cmd, "r+") do |io|
|
158
325
|
if(block_given?)
|
159
326
|
return(yield(io))
|
160
327
|
else
|
@@ -163,6 +330,11 @@ module RSCM
|
|
163
330
|
end
|
164
331
|
end
|
165
332
|
end
|
166
|
-
|
333
|
+
|
334
|
+
def monotone_date(time)
|
335
|
+
return nil unless time
|
336
|
+
time.utc.strftime("%Y-%m-%dT%H:%M:%S")
|
337
|
+
end
|
338
|
+
|
167
339
|
end
|
168
340
|
end
|