perforce2svn 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +38 -0
  3. data/LICENSE +23 -0
  4. data/README.markdown +66 -0
  5. data/Rakefile +24 -0
  6. data/bin/perforce2svn +11 -0
  7. data/lib/VERSION.yml +6 -0
  8. data/lib/perforce2svn/cli.rb +117 -0
  9. data/lib/perforce2svn/environment.rb +66 -0
  10. data/lib/perforce2svn/errors.rb +16 -0
  11. data/lib/perforce2svn/logging.rb +35 -0
  12. data/lib/perforce2svn/mapping/analyzer.rb +30 -0
  13. data/lib/perforce2svn/mapping/branch_mapping.rb +32 -0
  14. data/lib/perforce2svn/mapping/commands.rb +75 -0
  15. data/lib/perforce2svn/mapping/help.txt +139 -0
  16. data/lib/perforce2svn/mapping/lexer.rb +101 -0
  17. data/lib/perforce2svn/mapping/mapping_file.rb +65 -0
  18. data/lib/perforce2svn/mapping/operation.rb +8 -0
  19. data/lib/perforce2svn/mapping/parser.rb +145 -0
  20. data/lib/perforce2svn/migrator.rb +71 -0
  21. data/lib/perforce2svn/perforce/commit_builder.rb +159 -0
  22. data/lib/perforce2svn/perforce/p4_depot.rb +69 -0
  23. data/lib/perforce2svn/perforce/perforce_file.rb +81 -0
  24. data/lib/perforce2svn/subversion/svn_repo.rb +156 -0
  25. data/lib/perforce2svn/subversion/svn_transaction.rb +136 -0
  26. data/lib/perforce2svn/version_range.rb +43 -0
  27. data/mjt.map +7 -0
  28. data/perforce2svn.gemspec +49 -0
  29. data/spec/integration/hamlet.txt +7067 -0
  30. data/spec/integration/madmen_icon_bigger.jpg +0 -0
  31. data/spec/integration/perforce/p4_depot_spec.rb +16 -0
  32. data/spec/integration/perforce/perforce_file.yml +4 -0
  33. data/spec/integration/perforce/perforce_file_spec.rb +19 -0
  34. data/spec/integration/subversion/svn_repo_spec.rb +93 -0
  35. data/spec/integration/subversion/svn_transaction_spec.rb +112 -0
  36. data/spec/perforce2svn/cli_spec.rb +61 -0
  37. data/spec/perforce2svn/mapping/analyzer_spec.rb +41 -0
  38. data/spec/perforce2svn/mapping/branch_mapping_spec.rb +40 -0
  39. data/spec/perforce2svn/mapping/lexer_spec.rb +43 -0
  40. data/spec/perforce2svn/mapping/parser_spec.rb +140 -0
  41. data/spec/perforce2svn/perforce/commit_builder_spec.rb +74 -0
  42. data/spec/perforce2svn/version_range_spec.rb +42 -0
  43. data/spec/spec_helpers.rb +44 -0
  44. metadata +230 -0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ perforce2svn (0.1.0)
5
+ choosy (>= 0.4.8)
6
+ log4r (>= 1.1.7)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ ZenTest (4.5.0)
12
+ autotest (4.4.6)
13
+ ZenTest (>= 4.4.1)
14
+ autotest-notification (2.3.1)
15
+ autotest (~> 4.3)
16
+ choosy (0.4.8)
17
+ diff-lcs (1.1.2)
18
+ log4r (1.1.9)
19
+ mocha (0.9.12)
20
+ rspec (2.5.0)
21
+ rspec-core (~> 2.5.0)
22
+ rspec-expectations (~> 2.5.0)
23
+ rspec-mocks (~> 2.5.0)
24
+ rspec-core (2.5.1)
25
+ rspec-expectations (2.5.0)
26
+ diff-lcs (~> 1.1.2)
27
+ rspec-mocks (2.5.0)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ ZenTest
34
+ autotest
35
+ autotest-notification
36
+ mocha
37
+ perforce2svn!
38
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c) 2011 Gabe McArthur
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ This Software shall be used for Good, not Evil.
@@ -0,0 +1,66 @@
1
+ # Perforce2Svn Migration Tool
2
+
3
+ This tool is designed to help you with relatively sophisticated migrations from a Perforce server into Subversion. The Subversion repository must be on the file system for this command to function correctly. If a repository does not exist, one will be created.
4
+
5
+ ## Prerequisites && Installation
6
+
7
+ This gem has only been tested on Ubuntu, though it should work on any Linux/Unix derivative with the correct binary libraries. It has only been tested on 1.8.7, though it should work on 1.8.6; it most certainly does not work at all under 1.9, particularly since the binary libraries are build against 1.8.
8
+
9
+ Sidenote: if you do run Ubuntu (or Debian), please do yourself a favor and install the latest from rubygems.org. Gem packaging and maintenance on Debian is complete crap and should not be trusted.
10
+
11
+ For this gem to function correctly, you will need to install certain tools on your own. You will need at least:
12
+
13
+ - <code>svnadmin</code>: Required for cleaning up stale transactions if you Ctrl-C during a migration.
14
+ - <code>p4</code>: The Perforce command line utility should be installed. You will need to log in with it (or p4v) before you can do any migration.
15
+
16
+ In addition to these console tools, you will need to install some gems on your own. First, the <code>p4ruby (>= 1.0.9)</code> gem is required. I would list it as a dependency in the gemspec, but I have yet to have it succesfully install itself on its own. You should try the following:
17
+
18
+ gem install p4ruby
19
+ # ... Watch it fail, record the FTP url so that you can go to the 10.2 sources
20
+ cd ${GEM_HOME}/gems/p4ruby-1.0.9/
21
+ ruby install.rb --version 10.2 --platform ${YOUR_PLATFORM_HERE}
22
+
23
+ Additionally, you will need to install the Subversion bindings for Ruby. These should install their code under RUBY_HOME. On Ubuntu, you can run:
24
+
25
+ apt-get install libsvn-ruby
26
+
27
+ I'm sure there are other binary packages out there for other platforms.
28
+
29
+ At this point, you can now install the main gem:
30
+
31
+ gem install perforce2svn
32
+
33
+ This installs the <code>perforce2svn</code> command line client onto your path.
34
+
35
+ ## Usage
36
+
37
+ The <code>perforce2svn</code> tool requires a mapping file that describes the migration activities that should occur. The general outline of a mapping file should look something like this:
38
+
39
+ # This is a comment.
40
+ migrate //depot/from/perforce/path /to/svn/trunk
41
+ migrate //depot/another/path /to/svn/trunk/subdir
42
+
43
+ # Post-migration actions can also occur:
44
+ copy /svn/path/in/tree /to/another/path
45
+ move /same/as/copy /but/gets/rid/of/old/path
46
+ delete /deletes/svn/file
47
+
48
+ # You can even add files at the end of a migration
49
+ update /live/file/in/tree.txt
50
+
51
+ The mapping file is fairly utilitarian, so you should feel free take what you need and leave the rest. There is a much more detailed discussion of the format of the mapping file and how to use it if you run <code>perforce2svn --mapping-file</code>.
52
+
53
+ The only quirky bit is the use of the <code>--live-path</code> flag, which points at the base directory of some directory structure that you want to use in your mapping file. In particularly large and complicated migrations and refactorings, this can be useful to update post-migration files so that any fixes that need to be immediately applied can be. This can be a bit of a time-saver if you have to manually test and re-test the migration operation several times to be absolutely sure that the code is in a usable state after the migration.
54
+
55
+ Running <code>perforce2svn --help</code> should give you most of the details that may be missing here.
56
+
57
+ ## Hacking
58
+
59
+ This tool is not the most refined. It doesn't handle branches and merges, really at all. It's probably better than the Perl tool (since you can see what's actually happenning), but it can still be a bit difficult to maintain full backward history.
60
+
61
+ If you want to add that functionality and take this project from me, go for it! I created an older version of this tool some time ago, and it should really be extended by somebody with more insight into Perforce's internals than myself. There are at least a few tests to see what you can do, and several of the tricky parts of the SVN API have been carefully masked for most operations.
62
+
63
+ In fact, you could take some of the work here and create other Perforce migration clients, since you can pull out file contents fairly easily.
64
+
65
+ May the Source be With You.
66
+
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift File.expand_path("../spec", __FILE__)
3
+
4
+ require 'fileutils'
5
+ require 'rake'
6
+ require 'rubygems'
7
+ require 'rspec/core/rake_task'
8
+ require 'choosy/rake'
9
+
10
+ task :default => :spec
11
+ task :test => [:integration, :spec]
12
+
13
+ desc "Run the RSpec tests for the main perforce2svn tree"
14
+ RSpec::Core::RakeTask.new(:spec) do |t|
15
+ t.pattern = 'spec/perforce2svn/**/*_spec.rb'
16
+ end
17
+
18
+ desc "Runs the integration tests"
19
+ RSpec::Core::RakeTask.new(:integration) do |t|
20
+ t.pattern = 'spec/integration/**/*_spec.rb'
21
+ end
22
+
23
+ desc "Clean up"
24
+ task :clean => ['gem:clean']
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'rubygems'
5
+ require 'perforce2svn/cli'
6
+ rescue LoadError
7
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
8
+ require 'perforce2svn/cli'
9
+ end
10
+
11
+ Perforce2Svn::CLI.new.execute!(ARGV)
@@ -0,0 +1,6 @@
1
+ ---
2
+ date: 01/04/2011
3
+ version:
4
+ tiny: 0
5
+ major: 0
6
+ minor: 7
@@ -0,0 +1,117 @@
1
+ require 'perforce2svn/errors'
2
+ require 'perforce2svn/version_range'
3
+ require 'perforce2svn/mapping/mapping_file'
4
+ require 'perforce2svn/migrator'
5
+ require 'choosy'
6
+
7
+ module Perforce2Svn
8
+ class CLI
9
+ include Choosy::Terminal
10
+
11
+ def execute!(args)
12
+ command.execute!(args)
13
+ end
14
+
15
+ def parse!(args, propagate=false)
16
+ command.parse!(args, propagate)
17
+ end
18
+
19
+ def command
20
+ ctx = self
21
+ version_info = Choosy::Version.load_from_parent
22
+ Choosy::Command.new :perforce2svn do
23
+ printer :manpage,
24
+ :version => version_info.to_s,
25
+ :date => version_info.date,
26
+ :manual => 'Perforce2Svn'
27
+
28
+ executor do |args, options|
29
+ migrator = Migrator.new(args[0], options)
30
+ migrator.run!
31
+ end
32
+
33
+ summary 'Migrates Perforce repository files into Subversion'
34
+
35
+ section 'DESCRIPTION' do
36
+ para 'This is a migration tool for migrating specific branches in Perforce into Subversion. It uses a mapping file to define the branch mappings at the directory level.'
37
+ para 'Because these migrations can be quite complex, and involve more sophisticated translations, this mapping file also allows for much more sophisticated operations on the Subversion repository after the migration, at least somewhat mitigating the difficulties in doing complex transformations in a unified way.'
38
+ para 'This utility assumes that you have already logged into Perforce and that the P4USER and P4PORT environment variables are set correctly.'
39
+ end
40
+
41
+ section 'OPTIONS' do
42
+ string :repository, "The path to the SVN repository. Required." do
43
+ required
44
+ depends_on :mapping_file # So that the mapping file will get printed and exit before this option gets validated
45
+ end
46
+ string :live_path, "The path to the files you want to add or update." do
47
+ validate do |args, options|
48
+ if !File.directory?(options[:live_path])
49
+ die "The --live-path must be a directory: #{options[:live_path]}"
50
+ end
51
+ end
52
+ end
53
+ string :changes, "The revision range to import from. This has the format START:END where START >= 1 and END can be any number or 'HEAD'." do
54
+ validate do |args, options|
55
+ options[:changes] = VersionRange.build(options[:changes])
56
+ end
57
+ end
58
+ boolean :skip_commands, "Skip the embedded commands in the configuration that are run after the perforce migration.", :short => '-u'
59
+ boolean :skip_perforce, "Skip the perforce migration, and run only the embedded commands.", :short => '-p'
60
+ boolean :analyze_only, "Only analyzes your mapping files for possible errors, but does not attempt to run the migration."
61
+
62
+ # Informative
63
+ para
64
+ boolean :debug, "Prints extra debug information"
65
+ version version_info.to_s
66
+ boolean :mapping_file, "Shows a detailed mapping file example" do
67
+ validate do |args, options|
68
+ ctx.page(Mapping::MappingFile.help_file)
69
+ exit 0
70
+ end
71
+ end
72
+ help
73
+ end
74
+
75
+ section 'FILES' do
76
+ para "Rather than define all of the file path mappings between the old Perforce repository and the new Subversion repository, this tool requires you to define a mapping file."
77
+ para "This file contains all of the relevant commands for migrating a complicated set of paths and branches from Perforce into Subversion."
78
+ para "The mapping file has an explicit syntax. Each line contains a directive and a set of arguments. Each argument is separated by a space, though that space can be escaped with a '\\' character. Lines can have comments starting with the '#' character and they continue to the end of the line."
79
+ para "Please use the '--mapping-help' command for more detailed information."
80
+ end
81
+
82
+ section 'ENVIRONMENT' do
83
+ para "P4USER - The username used to connect to the Perforce server."
84
+ para "P4PORT - The Perforce server name."
85
+ para "svnadmin - This tool must be installed."
86
+ para "p4 - The Perforce command line utility must be installed."
87
+ end
88
+
89
+ section 'BUGS AND LIMITATIONS' do
90
+ para "While this tool works better than the other, Perl-based tool, it has the same kind of limitations. Namely, it cannot track certain file changes well (like copying or moving). That requires information that isn't readily accessible."
91
+ para "Also, you may notice that some files are not exactly the same after the migration. The p4 utility occasionally adds newline characters at the end of the file stream, for inexplicable reasons, so sometimes there is an extra newline at the end of some text files. There's really no way around it."
92
+ end
93
+
94
+ section "LICENSE" do
95
+ para "This software is licensed under the MIT license. No warranty is expressed or implied by this software's use."
96
+ para "This software shall be used for Good, not Evil."
97
+ end
98
+
99
+ section "AUTHOR" do
100
+ para "Gabe McArthur <madeonamac@gmail.com>"
101
+ para "Submit fixes to: https://github.com/gabemc/perforce2svn"
102
+ end
103
+
104
+ arguments do
105
+ count 1
106
+ metaname 'MAPPING_FILE'
107
+
108
+ validate do |args, options|
109
+ if !File.file?(args[0])
110
+ die "the mapping file doesn't exist: #{args[0]}"
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,66 @@
1
+ require 'choosy/terminal'
2
+ require 'perforce2svn/perforce/p4_depot'
3
+
4
+ module Perforce2Svn
5
+ class Environment
6
+ include Choosy::Terminal
7
+
8
+ def check!
9
+ check_svnadmin
10
+ check_svnlib
11
+ check_perforce
12
+ check_p4lib
13
+ check_p4_liveness
14
+ end
15
+
16
+ private
17
+ def check_svnadmin
18
+ if !command_exists?('svnadmin')
19
+ die "Unable to locate svnadmin"
20
+ end
21
+ end
22
+
23
+ def check_svnlib
24
+ begin
25
+ require 'svn/core'
26
+ rescue LoadError
27
+ die "Unable to locate the native subversion bindings. Please install."
28
+ end
29
+ end
30
+
31
+ def check_perforce
32
+ user = check_env('P4USER')
33
+ server = check_env('P4PORT')
34
+
35
+ if !system('p4 help > /dev/null 2>&1')
36
+ die "Unable to locate or execute the 'p4' command. Is it on the PATH? Are you logged in?"
37
+ end
38
+ end
39
+
40
+ def check_env(name)
41
+ value = ENV[name]
42
+ if value.nil? || value.empty?
43
+ die "Unable to locate the '#{name}' environment variable"
44
+ end
45
+ value
46
+ end
47
+
48
+ def check_p4lib
49
+ begin
50
+ require 'P4'
51
+ if P4.identify =~ /\((\d+.\d+) API\)/
52
+ maj, min = $1.split(/\./)
53
+ if maj.to_i < 2009
54
+ die "Requires a P4 library version >= 2009.2"
55
+ end
56
+ end
57
+ rescue LoadError
58
+ die 'Unable to locate the P4 library, please install p4ruby'
59
+ end
60
+ end
61
+
62
+ def check_p4_liveness
63
+ Perforce::P4Depot.instance.connect!
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Perforce2Svn
3
+ Error = Class.new(RuntimeError)
4
+
5
+ # Configuration
6
+ ConfigurationError = Class.new(Perforce2Svn::Error)
7
+
8
+ # Subversion errors
9
+ SvnTransactionError = Class.new(Perforce2Svn::Error)
10
+
11
+ # Perforce
12
+ P4Error = Class.new(Perforce2Svn::Error)
13
+
14
+ # Mappings
15
+ MappingParserError = Class.new(Perforce2Svn::Error)
16
+ end
@@ -0,0 +1,35 @@
1
+ require 'log4r'
2
+
3
+ module Perforce2Svn
4
+ # A convenient module for mixins that allow to
5
+ # share the logging configuration everywhere
6
+ # easily
7
+ module Logging
8
+ @@log = nil
9
+
10
+ def self.configure(debug)
11
+ if @@log.nil?
12
+ @@log = Log4r::Logger.new 'perforce2svn'
13
+ @@log.outputters = Log4r::Outputter.stdout
14
+ @@log.level = if ENV['RSPEC_RUNNING']
15
+ Log4r::FATAL
16
+ elsif debug
17
+ Log4r::DEBUG
18
+ else
19
+ Log4r::INFO
20
+ end
21
+ Log4r::Outputter.stdout.formatter = Log4r::PatternFormatter.new(:pattern => "[%l]\t%M")
22
+ end
23
+ @@log
24
+ end
25
+
26
+ def self.log
27
+ @@log ||= configure(true)
28
+ end
29
+
30
+ # Meant for mixing into other classes for simplified logging
31
+ def log
32
+ @@log ||= Logging.log
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ require 'perforce2svn/logging'
2
+
3
+ module Perforce2Svn::Mapping
4
+ class Analyzer
5
+ include Perforce2Svn::Logging
6
+
7
+ def initialize(base_path)
8
+ @base_path = base_path
9
+ end
10
+
11
+ def check(commands)
12
+ # TODO: May want to make this more robust, like checking perforce paths and overlaps
13
+ succeeded = true
14
+ commands.each do |command|
15
+ if command.respond_to? :live_path
16
+ path = command.live_path
17
+ if path !~ /^\//
18
+ path = File.join(@base_path, path)
19
+ end
20
+
21
+ unless File.file? path
22
+ log.error("(line #{command.line_number}) The live path doesn't exist: #{command.live_path}")
23
+ succeeded = false
24
+ end
25
+ end
26
+ end
27
+ succeeded
28
+ end
29
+ end
30
+ end