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
@@ -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
|