perforce2svn 0.7.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/Gemfile +2 -0
- data/Gemfile.lock +38 -0
- data/LICENSE +23 -0
- data/README.markdown +66 -0
- data/Rakefile +24 -0
- data/bin/perforce2svn +11 -0
- data/lib/VERSION.yml +6 -0
- data/lib/perforce2svn/cli.rb +117 -0
- data/lib/perforce2svn/environment.rb +66 -0
- data/lib/perforce2svn/errors.rb +16 -0
- data/lib/perforce2svn/logging.rb +35 -0
- data/lib/perforce2svn/mapping/analyzer.rb +30 -0
- data/lib/perforce2svn/mapping/branch_mapping.rb +32 -0
- data/lib/perforce2svn/mapping/commands.rb +75 -0
- data/lib/perforce2svn/mapping/help.txt +139 -0
- data/lib/perforce2svn/mapping/lexer.rb +101 -0
- data/lib/perforce2svn/mapping/mapping_file.rb +65 -0
- data/lib/perforce2svn/mapping/operation.rb +8 -0
- data/lib/perforce2svn/mapping/parser.rb +145 -0
- data/lib/perforce2svn/migrator.rb +71 -0
- data/lib/perforce2svn/perforce/commit_builder.rb +159 -0
- data/lib/perforce2svn/perforce/p4_depot.rb +69 -0
- data/lib/perforce2svn/perforce/perforce_file.rb +81 -0
- data/lib/perforce2svn/subversion/svn_repo.rb +156 -0
- data/lib/perforce2svn/subversion/svn_transaction.rb +136 -0
- data/lib/perforce2svn/version_range.rb +43 -0
- data/mjt.map +7 -0
- data/perforce2svn.gemspec +49 -0
- data/spec/integration/hamlet.txt +7067 -0
- data/spec/integration/madmen_icon_bigger.jpg +0 -0
- data/spec/integration/perforce/p4_depot_spec.rb +16 -0
- data/spec/integration/perforce/perforce_file.yml +4 -0
- data/spec/integration/perforce/perforce_file_spec.rb +19 -0
- data/spec/integration/subversion/svn_repo_spec.rb +93 -0
- data/spec/integration/subversion/svn_transaction_spec.rb +112 -0
- data/spec/perforce2svn/cli_spec.rb +61 -0
- data/spec/perforce2svn/mapping/analyzer_spec.rb +41 -0
- data/spec/perforce2svn/mapping/branch_mapping_spec.rb +40 -0
- data/spec/perforce2svn/mapping/lexer_spec.rb +43 -0
- data/spec/perforce2svn/mapping/parser_spec.rb +140 -0
- data/spec/perforce2svn/perforce/commit_builder_spec.rb +74 -0
- data/spec/perforce2svn/version_range_spec.rb +42 -0
- data/spec/spec_helpers.rb +44 -0
- metadata +230 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'perforce2svn/errors'
|
2
|
+
require 'perforce2svn/mapping/operation'
|
3
|
+
|
4
|
+
module Perforce2Svn::Mapping
|
5
|
+
class BranchMapping < Operation
|
6
|
+
attr_reader :p4_path, :svn_path
|
7
|
+
|
8
|
+
def initialize(tok, p4_path, svn_path)
|
9
|
+
super(tok)
|
10
|
+
@p4_path = p4_path
|
11
|
+
@svn_path = svn_path
|
12
|
+
@path_match = /^#{@p4_path}/
|
13
|
+
end
|
14
|
+
|
15
|
+
def p4_dotted
|
16
|
+
@p4_path + '...'
|
17
|
+
end
|
18
|
+
|
19
|
+
def matches_perforce_path?(other_p4_path)
|
20
|
+
(other_p4_path =~ @path_match) != nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_svn_path(other_p4_path)
|
24
|
+
opath = other_p4_path.gsub(p4_path, svn_path)
|
25
|
+
opath.gsub!("%40", "@")
|
26
|
+
opath.gsub!("%23", "#")
|
27
|
+
opath.gsub!("%2a", "*")
|
28
|
+
opath.gsub!("%25", "%")
|
29
|
+
opath
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'perforce2svn/mapping/operation'
|
2
|
+
|
3
|
+
module Perforce2Svn::Mapping
|
4
|
+
class Command < Operation
|
5
|
+
# Must be implemented by subclasses
|
6
|
+
def execute!(svn_txn)
|
7
|
+
raise Exception, "Not implemented"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Copy < Command
|
12
|
+
attr_reader :svn_from, :svn_to
|
13
|
+
def initialize(tok,svn_from, svn_to)
|
14
|
+
super(tok)
|
15
|
+
@svn_from = svn_from
|
16
|
+
@svn_to = svn_to
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute!(svn_txn)
|
20
|
+
svn_txn.copy(@svn_from, @svn_to)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Mkdir < Command
|
25
|
+
attr_reader :svn_path
|
26
|
+
def initialize(tok, svn_path)
|
27
|
+
super(tok)
|
28
|
+
@svn_path = svn_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute!(svn_txn)
|
32
|
+
svn_txn.mkdir(@svn_path)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Move < Command
|
37
|
+
attr_reader :svn_from, :svn_to
|
38
|
+
def initialize(tok, svn_from, svn_to)
|
39
|
+
super(tok)
|
40
|
+
@svn_from = svn_from
|
41
|
+
@svn_to = svn_to
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute!(svn_txn)
|
45
|
+
svn_txn.move(@svn_from, @svn_to)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Update < Command
|
50
|
+
attr_reader :svn_path, :live_path
|
51
|
+
def initialize(tok, svn_path, live_path)
|
52
|
+
super(tok)
|
53
|
+
@svn_path = svn_path
|
54
|
+
@live_path = live_path
|
55
|
+
end
|
56
|
+
|
57
|
+
def execute!(svn_txn)
|
58
|
+
File.open(@live_path, 'r') do |fstream|
|
59
|
+
svn_txn.update(@svn_path, fstream)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Delete < Command
|
65
|
+
attr_reader :svn_path
|
66
|
+
def initialize(tok, svn_path)
|
67
|
+
super(tok)
|
68
|
+
@svn_path = svn_path
|
69
|
+
end
|
70
|
+
|
71
|
+
def execute!(svn_txn)
|
72
|
+
svn_txn.delete(@svn_path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
######################################################################
|
2
|
+
# MAPPING FILE
|
3
|
+
######################################################################
|
4
|
+
#
|
5
|
+
# This file has been colored for your convenience. Comments are in blue;
|
6
|
+
# commands in green.
|
7
|
+
#
|
8
|
+
# This is an example mapping file for describing a migration from Perforce
|
9
|
+
# into Subversion. Blank lines are ignored, and everything after the
|
10
|
+
# '#' character in a line is ignored.
|
11
|
+
|
12
|
+
######################################################################
|
13
|
+
# Migration Command
|
14
|
+
#
|
15
|
+
# There are several commands to be aware of, the most central of which is:
|
16
|
+
migrate //depot/perforce/path /trunk/absolute/path
|
17
|
+
|
18
|
+
# As you can see, the migrate command takes 2 arguments: the path to migrate
|
19
|
+
# from and the path to migrate to. It cannot handle individual files -- only
|
20
|
+
# directory structures.
|
21
|
+
#
|
22
|
+
# Multiple migrate commands can be given in this file, mapping from Perforce
|
23
|
+
# path to Subversion path. This is useful when migrating both 'trunk' and
|
24
|
+
# 'branches' inside a repository when a standard layout has not been supplied,
|
25
|
+
# or for more significant code refactorings.
|
26
|
+
#
|
27
|
+
# Each part of the command is separated by spaces, and multiple spaces are
|
28
|
+
# ignored.
|
29
|
+
#
|
30
|
+
# Commands don't necessarily need to be on the same line. If to continue a
|
31
|
+
# command on the next line, you can add a '\' character to the end of the
|
32
|
+
# line. So, the above command could have been written:
|
33
|
+
migrate //depot/perforce/path \
|
34
|
+
/trunk/absolute/path
|
35
|
+
|
36
|
+
# If you have spaces in your path names, those spaces need to be escaped in a
|
37
|
+
# similar manner:
|
38
|
+
migrate //depot/another\ path\ with/spaces \
|
39
|
+
/to\ a\ path/with/spaces
|
40
|
+
|
41
|
+
######################################################################
|
42
|
+
# Directives
|
43
|
+
#
|
44
|
+
# There are several directives, all of which are optional.
|
45
|
+
|
46
|
+
## author
|
47
|
+
# The first directive is 'author'. This directive informs the migration
|
48
|
+
# that when the commands (discussed below) are committed to the repository,
|
49
|
+
# the name of the author should be used.
|
50
|
+
author gabe.mcarthur
|
51
|
+
# Thus, when the log is scanned in the future in the SVN repository, this
|
52
|
+
# will be the author of the commands. If no author is given, the system
|
53
|
+
# will put in 'Perforce2Svn Migration Tool'
|
54
|
+
|
55
|
+
## message
|
56
|
+
# The next directive is like the first. It attaches the given log message
|
57
|
+
# to the commit that occurs when the commands (discussed below) are
|
58
|
+
# executed.
|
59
|
+
message This is a message about all of the cool things that will be \
|
60
|
+
will be done to the repository after the primary Perforce \
|
61
|
+
migration is complete. No need to worry about the multiple lines \
|
62
|
+
here, as long as the line is terminated with a '\' character.
|
63
|
+
# If no message directive is given, the system supplies 'Perforce migration.'
|
64
|
+
|
65
|
+
## svn-prefix
|
66
|
+
# There is a special directive that you need to be aware of:
|
67
|
+
svn-prefix /project/trunk
|
68
|
+
|
69
|
+
# This directive informs the mapping system that every SVN path farther down
|
70
|
+
# in the file that don't start with a '/' character will have this path prefix
|
71
|
+
# pre-pended.
|
72
|
+
#
|
73
|
+
# Paths that start with a '/' are considered absolute and will not be altered.
|
74
|
+
#
|
75
|
+
# Thus, given the previous directive, the following commands are now
|
76
|
+
# equivalent:
|
77
|
+
migrate //depot/perforce/path /project/trunk/src/path
|
78
|
+
migrate //depot/perforce/path src/path
|
79
|
+
|
80
|
+
# This feature is a convenience so that you don't have to type out the full
|
81
|
+
# path each time, which can become a hassle in a big migration.
|
82
|
+
#
|
83
|
+
# Note that you can call the 'svn-prefix' directive multiple times, and each
|
84
|
+
# relative SVN path that follows will have the directly preceeding directive
|
85
|
+
# take precedence.
|
86
|
+
|
87
|
+
######################################################################
|
88
|
+
# Additional Commands
|
89
|
+
#
|
90
|
+
# The 'migrate' command is obviously the most essential, but this tool
|
91
|
+
# provides several other convenience commands to help you migrate more
|
92
|
+
# complex directory structures.
|
93
|
+
#
|
94
|
+
# Each of these commands is pretty much the same as their SVN counterparts
|
95
|
+
# (with a few noted exceptions).
|
96
|
+
|
97
|
+
## move
|
98
|
+
# Moves paths internally within SVN after the migration:
|
99
|
+
move original/svn/path.txt \
|
100
|
+
new/svn/path.txt
|
101
|
+
|
102
|
+
## delete
|
103
|
+
# Deletes paths internally after the migration:
|
104
|
+
delete some/perforce/file/thats/no/longer/needed.sh
|
105
|
+
|
106
|
+
## copy
|
107
|
+
# Copies SVN paths, retaining their history within the tree after a
|
108
|
+
# migration
|
109
|
+
copy /some/absolute/file.txt \
|
110
|
+
to/another/needed/location.txt
|
111
|
+
|
112
|
+
## mkdir
|
113
|
+
# Creates empty directories. By default, directory structures are created for
|
114
|
+
# you when you copy or move files, but this is occasionally useful when you
|
115
|
+
# want to set up the standard Subversion layout.
|
116
|
+
mkdir /project/branches
|
117
|
+
mkdir /project/tags
|
118
|
+
|
119
|
+
## update
|
120
|
+
# The 'update' command is a bit weird, as it will look at the 'live' sources that
|
121
|
+
# you have edited externally and passed in via the '--live-path' flag.
|
122
|
+
# This migration tool will add the sources listed below that live path to the
|
123
|
+
# SVN repository. If the file doesn't exist in the Subversion repository, it
|
124
|
+
# will be created.
|
125
|
+
#
|
126
|
+
# Obviously, this is an optional command, and if you don't need it, you don't
|
127
|
+
# need to supply the '--live-path' flag. If you have this directive, and the
|
128
|
+
# '--live-path' flag isn't present, the migration will fail and ask you for the
|
129
|
+
# live path
|
130
|
+
#
|
131
|
+
# The use case for this option lies in that not all migrations happen at once.
|
132
|
+
# In particularly large refactorings, it may be necessary to update certain
|
133
|
+
# files that rely upon the new source control layout. This is a facility for
|
134
|
+
# adding those files transparently at the end of a migration, so that you don't
|
135
|
+
# have to add them manually. The mapping analysis tool will flag if any addition
|
136
|
+
# doesn't exist before the migration continues.
|
137
|
+
update some/live/path/pom.xml
|
138
|
+
update some/path/that/exists/in/live/and/in/svn/Klass.java
|
139
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
module Perforce2Svn
|
3
|
+
module Mapping
|
4
|
+
class Token
|
5
|
+
attr_reader :name, :args, :line_number
|
6
|
+
|
7
|
+
def initialize(name, args, line_number)
|
8
|
+
@name = name.gsub(/-/, '_')
|
9
|
+
@args = args
|
10
|
+
@line_number = line_number
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](index)
|
14
|
+
@args[index]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"{@(#{line_number}) name: #{name}; args: #{args.join(' ')} }"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Lexer
|
23
|
+
def initialize(content)
|
24
|
+
@content = content
|
25
|
+
end
|
26
|
+
|
27
|
+
# Yields each parsed line of the configuration
|
28
|
+
def each(&block)
|
29
|
+
if not block_given?
|
30
|
+
raise ArgumentError, "Requires a block"
|
31
|
+
end
|
32
|
+
|
33
|
+
lines = @content.readlines
|
34
|
+
i = 1
|
35
|
+
continues_from_previous = false
|
36
|
+
previous = []
|
37
|
+
|
38
|
+
lines.each do |line|
|
39
|
+
parts= tokenize(line)
|
40
|
+
|
41
|
+
will_continue_on_next_line = false
|
42
|
+
if line =~ /\\\s*$/
|
43
|
+
will_continue_on_next_line = true
|
44
|
+
end
|
45
|
+
|
46
|
+
if parts.length > 0
|
47
|
+
if continues_from_previous
|
48
|
+
previous << parts
|
49
|
+
else
|
50
|
+
previous = []
|
51
|
+
name = parts.shift
|
52
|
+
yield Token.new(name, parts, i)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
continues_from_previous = will_continue_on_next_line
|
57
|
+
i += 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Reads a line for all of the possible tokens
|
62
|
+
def tokenize(line)
|
63
|
+
chars = line.scan(/./)
|
64
|
+
parts = []
|
65
|
+
|
66
|
+
part = ""
|
67
|
+
i = 0
|
68
|
+
while i < chars.length
|
69
|
+
current = chars[i]
|
70
|
+
if current == '\\'
|
71
|
+
nxt = chars[i + 1]
|
72
|
+
if nxt == ' '
|
73
|
+
i += 1
|
74
|
+
part << ' '
|
75
|
+
elsif nxt == "\n"
|
76
|
+
break
|
77
|
+
end
|
78
|
+
elsif current == ' '
|
79
|
+
if part.length > 0
|
80
|
+
parts << part
|
81
|
+
part = ""
|
82
|
+
end
|
83
|
+
elsif current == '#'
|
84
|
+
break
|
85
|
+
else
|
86
|
+
part << current
|
87
|
+
end
|
88
|
+
|
89
|
+
i += 1
|
90
|
+
end
|
91
|
+
|
92
|
+
if not parts[-1].eql?(part)
|
93
|
+
parts << part
|
94
|
+
end
|
95
|
+
|
96
|
+
parts.delete_if {|p| p =~ /^\s*$/}
|
97
|
+
parts
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'perforce2svn/logging'
|
2
|
+
require 'perforce2svn/mapping/parser'
|
3
|
+
require 'perforce2svn/mapping/analyzer'
|
4
|
+
require 'choosy/terminal'
|
5
|
+
require 'choosy/printing/color'
|
6
|
+
|
7
|
+
module Perforce2Svn::Mapping
|
8
|
+
class MappingFile
|
9
|
+
|
10
|
+
def self.help_file
|
11
|
+
map_file = File.join(File.dirname(__FILE__), 'help.txt')
|
12
|
+
contents = ""
|
13
|
+
color = Choosy::Printing::Color.new
|
14
|
+
File.open(map_file, 'r') do |file|
|
15
|
+
file.each_line do |line|
|
16
|
+
contents << if line =~ /^#/
|
17
|
+
color.blue(line)
|
18
|
+
elsif line =~ /^([\w-]+)(.*)/
|
19
|
+
color.green($1) + $2 + "\n"
|
20
|
+
else
|
21
|
+
line
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
contents
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :mappings, :commands, :author, :message
|
30
|
+
|
31
|
+
def initialize(mapping_file, options)
|
32
|
+
load_mapping_file(mapping_file, options)
|
33
|
+
analyze(mapping_file)
|
34
|
+
|
35
|
+
if options[:analysis_only]
|
36
|
+
exit 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def load_mapping_file(migration_file, options)
|
42
|
+
migration = nil
|
43
|
+
File.open(migration_file, 'r') do |file|
|
44
|
+
parser = Parser.new
|
45
|
+
migration = parser.parse!(file, options[:live_path])
|
46
|
+
end
|
47
|
+
|
48
|
+
if migration[:failed]
|
49
|
+
Choosy::Terminal.die "Parsing the mapping file failed"
|
50
|
+
end
|
51
|
+
|
52
|
+
@mappings = migration[:mappings]
|
53
|
+
@commands = migration[:commands]
|
54
|
+
@author = migration[:author]
|
55
|
+
@message = migration[:message]
|
56
|
+
end
|
57
|
+
|
58
|
+
def analyze(mapping_file)
|
59
|
+
analyzer = Analyzer.new(File.dirname(mapping_file))
|
60
|
+
if !analyzer.check(@commands) || !analyzer.check(@mappings)
|
61
|
+
Choosy::Terminal.die "Analysis of mapping file failed"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'perforce2svn/logging'
|
2
|
+
require 'perforce2svn/mapping/lexer'
|
3
|
+
require 'perforce2svn/mapping/commands'
|
4
|
+
require 'perforce2svn/mapping/branch_mapping'
|
5
|
+
|
6
|
+
module Perforce2Svn::Mapping
|
7
|
+
class Parser
|
8
|
+
include Perforce2Svn::Logging
|
9
|
+
|
10
|
+
def parse!(content, live_path)
|
11
|
+
raise ArgumentError, "The content must respond to 'readlines'" unless content.respond_to? :readlines
|
12
|
+
|
13
|
+
live_path = live_path.dup
|
14
|
+
if !live_path.nil? && live_path !~ /\/$/
|
15
|
+
live_path << '/'
|
16
|
+
end
|
17
|
+
|
18
|
+
ctx = {
|
19
|
+
:failed => false,
|
20
|
+
:commands => [],
|
21
|
+
:mappings => [],
|
22
|
+
:svn_prefix => '',
|
23
|
+
:live_path => live_path,
|
24
|
+
:author => 'Perforce2Svn Migration Tool',
|
25
|
+
:message => 'Perforce Migration'
|
26
|
+
}
|
27
|
+
|
28
|
+
Lexer.new(content).each do |tok|
|
29
|
+
handle(tok, ctx)
|
30
|
+
end
|
31
|
+
ctx
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def handle(tok, ctx)
|
36
|
+
if private_methods.include? tok.name
|
37
|
+
send(tok.name, tok, ctx)
|
38
|
+
else
|
39
|
+
log.error "(line: #{tok.line_number}) Unknown directive: '#{tok.name}'"
|
40
|
+
ctx[:failed] = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def author(tok, ctx)
|
45
|
+
ctx[:author] = tok.args.join(' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
def message(tok, ctx)
|
49
|
+
ctx[:message] = tok.args.join(' ')
|
50
|
+
end
|
51
|
+
|
52
|
+
def copy(tok, ctx)
|
53
|
+
return unless args_ok?(2, tok, ctx)
|
54
|
+
ctx[:commands] << Copy.new(tok, format_svn_path(tok, tok[0], ctx), format_svn_path(tok, tok[1], ctx))
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete(tok, ctx)
|
58
|
+
return unless args_ok?(1, tok, ctx)
|
59
|
+
ctx[:commands] << Delete.new(tok, format_svn_path(tok, tok[0], ctx))
|
60
|
+
end
|
61
|
+
|
62
|
+
def mkdir(tok, ctx)
|
63
|
+
return unless args_ok?(1, tok, ctx)
|
64
|
+
ctx[:commands] << Mkdir.new(tok, format_svn_path(tok, tok[0], ctx))
|
65
|
+
end
|
66
|
+
|
67
|
+
def move(tok, ctx)
|
68
|
+
return unless args_ok?(2, tok, ctx)
|
69
|
+
ctx[:commands] << Move.new(tok, format_svn_path(tok, tok[0], ctx), format_svn_path(tok, tok[1], ctx))
|
70
|
+
end
|
71
|
+
|
72
|
+
def update(tok, ctx)
|
73
|
+
return unless args_ok?(1, tok, ctx)
|
74
|
+
ctx[:commands] << Update.new(tok, format_svn_path(tok, tok[0], ctx), format_live_path(tok, tok[0], ctx))
|
75
|
+
end
|
76
|
+
|
77
|
+
def migrate(tok, ctx)
|
78
|
+
return unless args_ok?(2, tok, ctx)
|
79
|
+
|
80
|
+
p4_path = tok[0]
|
81
|
+
p4_path << '/' if not p4_path[-1].chr == '/'
|
82
|
+
svn_path = format_svn_path(tok, tok[1], ctx)
|
83
|
+
svn_path << '/' if not svn_path[-1].chr == '/'
|
84
|
+
|
85
|
+
if p4_path !~ %r|^//([^/]+/?)*$|
|
86
|
+
log.error "(line #{tok.line_number}) Perforce path was malformed: '#{p4_path}'"
|
87
|
+
ctx[:failed] = true
|
88
|
+
end
|
89
|
+
|
90
|
+
if svn_path !~ %r|^/([^/]+/?)*$|
|
91
|
+
log.error "(line #{tok.line_number}) Subversion path was malformed: '#{svn_path}'"
|
92
|
+
ctx[:failed] = true
|
93
|
+
end
|
94
|
+
|
95
|
+
ctx[:mappings] << BranchMapping.new(tok, p4_path, svn_path)
|
96
|
+
end
|
97
|
+
|
98
|
+
def svn_prefix(tok, ctx)
|
99
|
+
return unless args_ok?(1, tok, ctx)
|
100
|
+
|
101
|
+
svn_prefix = tok[0]
|
102
|
+
if svn_prefix !~ /^\//
|
103
|
+
log.error "(line: #{tok.line_number}) 'svn-prefix' directive must start with a '/'"
|
104
|
+
ctx[:failed] = true
|
105
|
+
elsif svn_prefix !~ /\/$/
|
106
|
+
svn_prefix << '/'
|
107
|
+
end
|
108
|
+
|
109
|
+
ctx[:svn_prefix] = svn_prefix
|
110
|
+
end
|
111
|
+
|
112
|
+
def args_ok?(required_count, tok, ctx)
|
113
|
+
if tok.args.size != required_count
|
114
|
+
log.error "(line: #{tok.line_number}) '#{tok.name}' requires #{required_count} argument(s), but found #{tok.args.size}"
|
115
|
+
ctx[:failed] = true
|
116
|
+
return false
|
117
|
+
end
|
118
|
+
return true
|
119
|
+
end
|
120
|
+
|
121
|
+
def format_svn_path(tok, path, ctx)
|
122
|
+
if ctx[:svn_prefix].empty? && path !~ /^\//
|
123
|
+
log.error "(line: #{tok.line_number}) No 'svn-prefix' defined, but a relative SVN path was used"
|
124
|
+
ctx[:failed] = true
|
125
|
+
nil
|
126
|
+
else
|
127
|
+
"#{ctx[:svn_prefix]}#{path}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def format_live_path(tok, path, ctx)
|
132
|
+
if ctx[:live_path].nil?
|
133
|
+
log.error "(line: #{tok.line_number}) The command '#{tok.name}' requires a live path, but none was given."
|
134
|
+
ctx[:failed] = true
|
135
|
+
nil
|
136
|
+
else
|
137
|
+
if path =~ /^\//
|
138
|
+
"#{ctx[:live_path].chomp('/')}#{path}"
|
139
|
+
else
|
140
|
+
"#{ctx[:live_path].chomp('/')}#{ctx[:svn_prefix]}#{path}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end#Parser
|
145
|
+
end
|