jeremylightsmith-piston 1.9.3
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/.gitignore +6 -0
- data/History.txt +6 -0
- data/License.txt +20 -0
- data/Manifest.txt +86 -0
- data/README.txt +136 -0
- data/Rakefile +4 -0
- data/bin/piston +5 -0
- data/config/hoe.rb +76 -0
- data/config/requirements.rb +18 -0
- data/lib/piston.rb +18 -0
- data/lib/piston/cli.rb +279 -0
- data/lib/piston/commands.rb +4 -0
- data/lib/piston/commands/base.rb +44 -0
- data/lib/piston/commands/import.rb +69 -0
- data/lib/piston/commands/info.rb +14 -0
- data/lib/piston/commands/lock_unlock.rb +21 -0
- data/lib/piston/commands/update.rb +28 -0
- data/lib/piston/git.rb +12 -0
- data/lib/piston/git/client.rb +77 -0
- data/lib/piston/git/commit.rb +70 -0
- data/lib/piston/git/repository.rb +63 -0
- data/lib/piston/git/working_copy.rb +63 -0
- data/lib/piston/repository.rb +57 -0
- data/lib/piston/revision.rb +48 -0
- data/lib/piston/svn.rb +14 -0
- data/lib/piston/svn/client.rb +88 -0
- data/lib/piston/svn/repository.rb +67 -0
- data/lib/piston/svn/revision.rb +74 -0
- data/lib/piston/svn/working_copy.rb +55 -0
- data/lib/piston/version.rb +9 -0
- data/lib/piston/working_copy.rb +129 -0
- data/lib/subclass_responsibility_error.rb +2 -0
- data/log/.gitignore +0 -0
- data/samples/common.rb +19 -0
- data/samples/import_git_git.rb +39 -0
- data/samples/import_git_svn.rb +36 -0
- data/samples/import_svn_git.rb +29 -0
- data/samples/import_svn_svn.rb +24 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +40 -0
- data/tasks/environment.rake +7 -0
- data/tasks/samples.rake +6 -0
- data/tasks/test.rake +30 -0
- data/tasks/website.rake +17 -0
- data/test/integration/test_import_git_git.rb +96 -0
- data/test/integration/test_import_git_svn.rb +131 -0
- data/test/integration/test_import_svn_git.rb +52 -0
- data/test/integration/test_import_svn_svn.rb +43 -0
- data/test/integration_helpers.rb +33 -0
- data/test/test_helper.rb +56 -0
- data/test/unit/git/commit/test_checkout.rb +30 -0
- data/test/unit/git/commit/test_each.rb +36 -0
- data/test/unit/git/commit/test_rememberance.rb +20 -0
- data/test/unit/git/commit/test_validation.rb +33 -0
- data/test/unit/git/repository/test_at.rb +22 -0
- data/test/unit/git/repository/test_basename.rb +12 -0
- data/test/unit/git/repository/test_branchanme.rb +15 -0
- data/test/unit/git/repository/test_guessing.rb +32 -0
- data/test/unit/git/working_copy/test_copying.rb +28 -0
- data/test/unit/git/working_copy/test_creation.rb +26 -0
- data/test/unit/git/working_copy/test_existence.rb +21 -0
- data/test/unit/git/working_copy/test_finalization.rb +18 -0
- data/test/unit/git/working_copy/test_guessing.rb +34 -0
- data/test/unit/git/working_copy/test_rememberance.rb +25 -0
- data/test/unit/svn/repository/test_at.rb +18 -0
- data/test/unit/svn/repository/test_basename.rb +24 -0
- data/test/unit/svn/repository/test_guessing.rb +45 -0
- data/test/unit/svn/revision/test_checkout.rb +27 -0
- data/test/unit/svn/revision/test_each.rb +21 -0
- data/test/unit/svn/revision/test_rememberance.rb +42 -0
- data/test/unit/svn/revision/test_validation.rb +49 -0
- data/test/unit/svn/working_copy/test_copying.rb +29 -0
- data/test/unit/svn/working_copy/test_creation.rb +19 -0
- data/test/unit/svn/working_copy/test_existence.rb +26 -0
- data/test/unit/svn/working_copy/test_finalization.rb +21 -0
- data/test/unit/svn/working_copy/test_guessing.rb +17 -0
- data/test/unit/svn/working_copy/test_rememberance.rb +26 -0
- data/test/unit/test_import.rb +13 -0
- data/test/unit/test_info.rb +36 -0
- data/test/unit/test_lock_unlock.rb +46 -0
- data/test/unit/test_repository.rb +50 -0
- data/test/unit/test_revision.rb +30 -0
- data/test/unit/working_copy/test_guessing.rb +34 -0
- data/test/unit/working_copy/test_info.rb +13 -0
- data/test/unit/working_copy/test_rememberance.rb +48 -0
- data/test/unit/working_copy/test_validate.rb +68 -0
- data/website/index.html +11 -0
- data/website/index.txt +39 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +228 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require "piston/revision"
|
|
2
|
+
|
|
3
|
+
module Piston
|
|
4
|
+
class Repository
|
|
5
|
+
class UnhandledUrl < RuntimeError; end
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def logger
|
|
9
|
+
@@logger ||= Log4r::Logger["handler"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def guess(url)
|
|
13
|
+
logger.info {"Guessing the repository type of #{url.inspect}"}
|
|
14
|
+
|
|
15
|
+
handler = handlers.detect do |handler|
|
|
16
|
+
logger.debug {"Asking #{handler}"}
|
|
17
|
+
handler.understands_url?(url)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
raise UnhandledUrl unless handler
|
|
21
|
+
|
|
22
|
+
handler.new(url)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@@handlers = Array.new
|
|
26
|
+
def add_handler(handler)
|
|
27
|
+
@@handlers << handler
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def handlers
|
|
31
|
+
@@handlers
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_reader :url
|
|
36
|
+
|
|
37
|
+
def initialize(url)
|
|
38
|
+
@url = url
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def logger
|
|
42
|
+
self.class.logger
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def at(revision)
|
|
46
|
+
raise SubclassResponsibilityError, "Piston::Repository#at should be implemented by a subclass."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_s
|
|
50
|
+
"Piston::Repository(#{@url})"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def ==(other)
|
|
54
|
+
url == other.url
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Piston
|
|
2
|
+
class Revision
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
def logger
|
|
7
|
+
@@logger ||= Log4r::Logger["handler"]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :repository, :revision, :recalled_values
|
|
12
|
+
|
|
13
|
+
def initialize(repository, revision, recalled_values={})
|
|
14
|
+
@repository, @revision, @recalled_values = repository, revision, recalled_values
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def name
|
|
18
|
+
@revision
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def logger
|
|
22
|
+
self.class.logger
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
"Piston::Revision(#{@repository.url}@#{@revision})"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Retrieve a copy of this repository into +dir+.
|
|
30
|
+
def checkout_to(dir)
|
|
31
|
+
logger.debug {"Checking out #{@repository}@#{@revision} into #{dir}"}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# What values does this revision want to remember for the future ?
|
|
35
|
+
def remember_values
|
|
36
|
+
logger.debug {"Generating remember values"}
|
|
37
|
+
{}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Yields each file of this revision in turn to our caller.
|
|
41
|
+
def each
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Copies +relpath+ (relative to ourselves) to +abspath+ (an absolute path).
|
|
45
|
+
def copy_to(relpath, abspath)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/piston/svn.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "piston/svn/client"
|
|
2
|
+
require "piston/svn/repository"
|
|
3
|
+
require "piston/svn/revision"
|
|
4
|
+
require "piston/svn/working_copy"
|
|
5
|
+
|
|
6
|
+
module Piston
|
|
7
|
+
module Svn
|
|
8
|
+
ROOT = "piston:root"
|
|
9
|
+
UUID = "piston:uuid"
|
|
10
|
+
REMOTE_REV = "piston:remote-revision"
|
|
11
|
+
LOCAL_REV = "piston:local-revision"
|
|
12
|
+
LOCKED = "piston:locked"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "singleton"
|
|
2
|
+
|
|
3
|
+
module Piston
|
|
4
|
+
module Svn
|
|
5
|
+
class Client
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
class CommandError < RuntimeError; end
|
|
9
|
+
class Failed < CommandError; end
|
|
10
|
+
class BadCommand < CommandError; end
|
|
11
|
+
|
|
12
|
+
def logger
|
|
13
|
+
@logger ||= Log4r::Logger["handler::client"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def out_logger
|
|
17
|
+
@out_logger ||= Log4r::Logger["handler::client::out"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def svnadmin(*args)
|
|
21
|
+
run_cmd :svnadmin, *args
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def svn(*args)
|
|
25
|
+
run_cmd :svn, *args
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def svnlook(*args)
|
|
29
|
+
run_cmd :svnlook, *args
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def svnversion(*args)
|
|
33
|
+
run_cmd :svnversion, *args
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def run_cmd(executable, *args)
|
|
38
|
+
args.collect! {|arg| arg =~ /\s|\*|\?|"|\n|\r/ ? %Q('#{arg}') : arg}
|
|
39
|
+
args.collect! {|arg| arg ? arg : '""'}
|
|
40
|
+
cmd = %Q|#{executable} #{args.join(' ')}|
|
|
41
|
+
logger.debug {"> " + cmd}
|
|
42
|
+
|
|
43
|
+
original_language = ENV["LANGUAGE"]
|
|
44
|
+
begin
|
|
45
|
+
ENV["LANGUAGE"] = "C"
|
|
46
|
+
value = run_real(cmd)
|
|
47
|
+
out_logger.info {"< " + value} unless (value || "").strip.empty?
|
|
48
|
+
return value
|
|
49
|
+
ensure
|
|
50
|
+
ENV["LANGUAGE"] = original_language
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
begin
|
|
55
|
+
raise LoadError, "Not implemented on Win32 machines" if RUBY_PLATFORM =~ /mswin32/
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
require "rubygems"
|
|
59
|
+
rescue LoadError
|
|
60
|
+
# NOP -- attempt to load without Rubygems
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
require "open4"
|
|
64
|
+
|
|
65
|
+
def run_real(cmd)
|
|
66
|
+
begin
|
|
67
|
+
pid, stdin, stdout, stderr = Open4::popen4(cmd)
|
|
68
|
+
_, cmdstatus = Process.waitpid2(pid)
|
|
69
|
+
raise CommandError, "#{cmd.inspect} exited with status: #{cmdstatus.exitstatus}\n#{stderr.read}" unless cmdstatus.success?
|
|
70
|
+
return stdout.read
|
|
71
|
+
rescue Errno::ENOENT
|
|
72
|
+
raise BadCommand, cmd.inspect
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
rescue LoadError
|
|
77
|
+
# On platforms where open4 is unavailable, we fallback to running using
|
|
78
|
+
# the backtick method of Kernel.
|
|
79
|
+
def run_real(cmd)
|
|
80
|
+
out = `#{cmd}`
|
|
81
|
+
raise BadCommand, cmd.inspect if $?.exitstatus == 127
|
|
82
|
+
raise Failed, "#{cmd.inspect} exited with status: #{$?.exitstatus}" unless $?.success?
|
|
83
|
+
out
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
|
|
3
|
+
module Piston
|
|
4
|
+
module Svn
|
|
5
|
+
class Repository < Piston::Repository
|
|
6
|
+
# Register ourselves as a repository handler
|
|
7
|
+
Piston::Repository.add_handler self
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def understands_url?(url)
|
|
11
|
+
uri = URI.parse(url)
|
|
12
|
+
case uri.scheme
|
|
13
|
+
when "svn", /^svn\+/
|
|
14
|
+
true
|
|
15
|
+
when "http", "https", "file"
|
|
16
|
+
# Have to contact server to know
|
|
17
|
+
result = svn(:info, url) rescue :failed
|
|
18
|
+
result == :failed ? false : true
|
|
19
|
+
else
|
|
20
|
+
# Don't know how to handle this scheme.
|
|
21
|
+
# Let someone else handle it
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def client
|
|
26
|
+
@@client ||= Piston::Svn::Client.instance
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def svn(*args)
|
|
30
|
+
client.svn(*args)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def repository_type
|
|
34
|
+
'svn'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def svn(*args)
|
|
39
|
+
self.class.svn(*args)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def at(revision)
|
|
43
|
+
if revision.respond_to?(:keys) then
|
|
44
|
+
rev = revision[Piston::Svn::REMOTE_REV]
|
|
45
|
+
Piston::Svn::Revision.new(self, rev, revision)
|
|
46
|
+
else
|
|
47
|
+
case
|
|
48
|
+
when revision == :head
|
|
49
|
+
Piston::Svn::Revision.new(self, "HEAD")
|
|
50
|
+
when revision.to_i != 0
|
|
51
|
+
Piston::Svn::Revision.new(self, revision.to_i)
|
|
52
|
+
else
|
|
53
|
+
raise ArgumentError, "Invalid revision argument: #{revision.inspect}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def basename
|
|
59
|
+
if self.url =~ /trunk|branches|tags/ then
|
|
60
|
+
self.url.sub(%r{/(?:trunk|branches|tags).*$}, "").split("/").last
|
|
61
|
+
else
|
|
62
|
+
self.url.split("/").last
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require "piston/revision"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
module Piston
|
|
5
|
+
module Svn
|
|
6
|
+
class Revision < Piston::Revision
|
|
7
|
+
class InvalidRevision < RuntimeError; end
|
|
8
|
+
class RepositoryMoved < InvalidRevision; end
|
|
9
|
+
class UuidChanged < InvalidRevision; end
|
|
10
|
+
|
|
11
|
+
def client
|
|
12
|
+
@client ||= Piston::Svn::Client.instance
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def svn(*args)
|
|
16
|
+
client.svn(*args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def name
|
|
20
|
+
"r#{revision}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def checkout_to(path)
|
|
24
|
+
@wcpath = path.kind_of?(Pathname) ? path : Pathname.new(path)
|
|
25
|
+
answer = svn(:checkout, "--revision", revision, repository.url, path)
|
|
26
|
+
if answer =~ /Checked out revision (\d+)[.]/ then
|
|
27
|
+
if revision == "HEAD" then
|
|
28
|
+
@revision = $1.to_i
|
|
29
|
+
elsif revision != $1.to_i then
|
|
30
|
+
raise Failed, "Did not get the revision I wanted to checkout. Subversion checked out #{$1}, I wanted #{revision}"
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
raise Failed, "Could not checkout revision #{revision} from #{repository.url} to #{path}\n#{answer}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def remember_values
|
|
38
|
+
str = svn(:info, "--revision", revision, repository.url)
|
|
39
|
+
raise Failed, "Could not get 'svn info' from #{repository.url} at revision #{revision}" if str.nil? || str.chomp.strip.empty?
|
|
40
|
+
info = YAML.load(str)
|
|
41
|
+
{ Piston::Svn::UUID => info["Repository UUID"],
|
|
42
|
+
Piston::Svn::REMOTE_REV => info["Revision"]}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def each
|
|
46
|
+
raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @wcpath
|
|
47
|
+
|
|
48
|
+
svn(:ls, "--recursive", @wcpath).each do |relpath|
|
|
49
|
+
next if relpath =~ %r{/$}
|
|
50
|
+
yield relpath.chomp
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def copy_to(relpath, abspath)
|
|
55
|
+
raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @wcpath
|
|
56
|
+
|
|
57
|
+
Pathname.new(abspath).dirname.mkpath
|
|
58
|
+
FileUtils.cp(@wcpath + relpath, abspath)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate!
|
|
62
|
+
data = svn(:info, "--revision", revision, repository.url)
|
|
63
|
+
info = YAML.load(data)
|
|
64
|
+
actual_uuid = info["Repository UUID"]
|
|
65
|
+
raise RepositoryMoved, "Repository at #{repository.url} does not exist anymore:\n#{data}" if actual_uuid.blank?
|
|
66
|
+
raise UuidChanged, "Expected repository at #{repository.url} to have UUID #{recalled_uuid} but found #{actual_uuid}" if recalled_uuid != actual_uuid
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def recalled_uuid
|
|
70
|
+
recalled_values[Piston::Svn::UUID]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
module Piston
|
|
4
|
+
module Svn
|
|
5
|
+
class WorkingCopy < Piston::WorkingCopy
|
|
6
|
+
# Register ourselves as a handler for working copies
|
|
7
|
+
Piston::WorkingCopy.add_handler self
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def understands_dir?(dir)
|
|
11
|
+
result = svn(:info, dir) rescue :failed
|
|
12
|
+
result == :failed ? false : true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def client
|
|
16
|
+
@@client ||= Piston::Svn::Client.instance
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def svn(*args)
|
|
20
|
+
client.svn(*args)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def svn(*args)
|
|
25
|
+
self.class.svn(*args)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def exist?
|
|
29
|
+
result = svn(:info, path) rescue :failed
|
|
30
|
+
logger.debug {"result: #{result.inspect}"}
|
|
31
|
+
return false if result == :failed
|
|
32
|
+
return false if result.nil? || result.chomp.strip.empty?
|
|
33
|
+
return true if YAML.load(result).has_key?("Path")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create
|
|
37
|
+
svn(:mkdir, path)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def after_remember(path)
|
|
41
|
+
info = svn(:info, path)
|
|
42
|
+
return unless info =~ /\(not a versioned resource\)/i || info.empty?
|
|
43
|
+
svn(:add, path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def finalize
|
|
47
|
+
targets = []
|
|
48
|
+
Dir[path + "*"].each do |item|
|
|
49
|
+
svn(:add, item)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module Piston
|
|
2
|
+
class WorkingCopy
|
|
3
|
+
class UnhandledWorkingCopy < RuntimeError; end
|
|
4
|
+
class NotWorkingCopy < RuntimeError; end
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def logger
|
|
8
|
+
@@logger ||= Log4r::Logger["handler"]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def guess(path)
|
|
12
|
+
path = path.kind_of?(Pathname) ? path : Pathname.new(path.to_s)
|
|
13
|
+
logger.info {"Guessing the working copy type of #{path.inspect}"}
|
|
14
|
+
handler = handlers.detect do |handler|
|
|
15
|
+
logger.debug {"Asking #{handler.name} if it understands #{path}"}
|
|
16
|
+
handler.understands_dir?(path)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
raise UnhandledWorkingCopy, "Don't know what working copy type #{path} is." if handler.nil?
|
|
20
|
+
handler.new(path)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@@handlers = Array.new
|
|
24
|
+
def add_handler(handler)
|
|
25
|
+
@@handlers << handler
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def handlers
|
|
29
|
+
@@handlers
|
|
30
|
+
end
|
|
31
|
+
private :handlers
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_reader :path
|
|
35
|
+
|
|
36
|
+
def initialize(path)
|
|
37
|
+
@path = path.kind_of?(Pathname) ? path : Pathname.new(path)
|
|
38
|
+
logger.debug {"Initialized on #{@path}"}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def logger
|
|
42
|
+
self.class.logger
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_s
|
|
46
|
+
"Piston::WorkingCopy(#{@path})"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def exist?
|
|
50
|
+
@path.exist? && @path.directory?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def pistonized?
|
|
54
|
+
yaml_path.exist? && yaml_path.file?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate!
|
|
58
|
+
raise NotWorkingCopy unless self.pistonized?
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Creates the initial working copy for pistonizing a new repository.
|
|
63
|
+
def create
|
|
64
|
+
logger.debug {"Creating working copy at #{path}"}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Copy files from +revision+. +revision+ must
|
|
68
|
+
# #respond_to?(:each), and return each file that is to be copied.
|
|
69
|
+
# Only files must be returned.
|
|
70
|
+
#
|
|
71
|
+
# Each item yielded by Revision#each must be a relative path.
|
|
72
|
+
#
|
|
73
|
+
# WorkingCopy will call Revision#copy_to with the full path to where the
|
|
74
|
+
# file needs to be copied.
|
|
75
|
+
def copy_from(revision)
|
|
76
|
+
revision.each do |relpath|
|
|
77
|
+
target = path + relpath
|
|
78
|
+
target.dirname.mkdir rescue nil
|
|
79
|
+
|
|
80
|
+
logger.debug {"Copying #{relpath} to #{target}"}
|
|
81
|
+
revision.copy_to(relpath, target)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Stores a Hash of values that can be retrieved later.
|
|
86
|
+
def remember(values, handler_values)
|
|
87
|
+
values["format"] = 1
|
|
88
|
+
|
|
89
|
+
# Stringify keys
|
|
90
|
+
values.keys.each do |key|
|
|
91
|
+
values[key.to_s] = values.delete(key)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
logger.debug {"Remembering #{values.inspect} as well as #{handler_values.inspect}"}
|
|
95
|
+
File.open(yaml_path, "wb") do |f|
|
|
96
|
+
f.write(values.merge("handler" => handler_values).to_yaml)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
logger.debug {"Calling \#after_remember on #{yaml_path}"}
|
|
100
|
+
after_remember(yaml_path)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Callback after #remember is done, to do whatever the
|
|
104
|
+
# working copy needs to do with the file.
|
|
105
|
+
def after_remember(path)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Recalls a Hash of values from the working copy.
|
|
109
|
+
def recall
|
|
110
|
+
YAML.load(File.read(yaml_path))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def finalize
|
|
114
|
+
logger.debug {"Finalizing #{path}"}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns basic information about this working copy.
|
|
118
|
+
def info
|
|
119
|
+
recall
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
protected
|
|
124
|
+
# The path to the piston YAML file.
|
|
125
|
+
def yaml_path
|
|
126
|
+
path + ".piston.yml"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|