piston 1.4.0 → 2.0.1

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.
Files changed (85) hide show
  1. data/History.txt +24 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +109 -0
  4. data/{README → README.txt} +14 -10
  5. data/VERSION.yml +4 -0
  6. data/bin/piston +3 -8
  7. data/lib/piston/cli.rb +121 -0
  8. data/lib/piston/commands/base.rb +44 -0
  9. data/lib/piston/commands/convert.rb +23 -71
  10. data/lib/piston/commands/diff.rb +14 -46
  11. data/lib/piston/commands/import.rb +48 -57
  12. data/lib/piston/commands/info.rb +24 -0
  13. data/lib/piston/commands/lock_unlock.rb +26 -0
  14. data/lib/piston/commands/status.rb +29 -54
  15. data/lib/piston/commands/update.rb +35 -122
  16. data/lib/piston/commands/upgrade.rb +26 -0
  17. data/lib/piston/commands.rb +4 -0
  18. data/lib/piston/git/client.rb +76 -0
  19. data/lib/piston/git/commit.rb +114 -0
  20. data/lib/piston/git/repository.rb +63 -0
  21. data/lib/piston/git/working_copy.rb +145 -0
  22. data/lib/piston/git.rb +13 -0
  23. data/lib/piston/repository.rb +61 -0
  24. data/lib/piston/revision.rb +83 -0
  25. data/lib/piston/svn/client.rb +88 -0
  26. data/lib/piston/svn/repository.rb +67 -0
  27. data/lib/piston/svn/revision.rb +112 -0
  28. data/lib/piston/svn/working_copy.rb +184 -0
  29. data/lib/piston/svn.rb +15 -0
  30. data/lib/piston/version.rb +9 -7
  31. data/lib/piston/working_copy.rb +334 -0
  32. data/lib/piston.rb +13 -64
  33. data/lib/subclass_responsibility_error.rb +2 -0
  34. data/test/integration_helpers.rb +36 -0
  35. data/test/spec_suite.rb +4 -0
  36. data/test/test_helper.rb +92 -0
  37. data/test/unit/git/commit/test_checkout.rb +31 -0
  38. data/test/unit/git/commit/test_each.rb +30 -0
  39. data/test/unit/git/commit/test_rememberance.rb +22 -0
  40. data/test/unit/git/commit/test_validation.rb +34 -0
  41. data/test/unit/git/repository/test_at.rb +23 -0
  42. data/test/unit/git/repository/test_basename.rb +12 -0
  43. data/test/unit/git/repository/test_branchanme.rb +15 -0
  44. data/test/unit/git/repository/test_guessing.rb +32 -0
  45. data/test/unit/git/working_copy/test_copying.rb +25 -0
  46. data/test/unit/git/working_copy/test_creation.rb +22 -0
  47. data/test/unit/git/working_copy/test_existence.rb +18 -0
  48. data/test/unit/git/working_copy/test_finalization.rb +15 -0
  49. data/test/unit/git/working_copy/test_guessing.rb +35 -0
  50. data/test/unit/git/working_copy/test_rememberance.rb +22 -0
  51. data/test/unit/svn/repository/test_at.rb +19 -0
  52. data/test/unit/svn/repository/test_basename.rb +24 -0
  53. data/test/unit/svn/repository/test_guessing.rb +45 -0
  54. data/test/unit/svn/revision/test_checkout.rb +28 -0
  55. data/test/unit/svn/revision/test_each.rb +22 -0
  56. data/test/unit/svn/revision/test_rememberance.rb +38 -0
  57. data/test/unit/svn/revision/test_validation.rb +50 -0
  58. data/test/unit/svn/working_copy/test_copying.rb +26 -0
  59. data/test/unit/svn/working_copy/test_creation.rb +16 -0
  60. data/test/unit/svn/working_copy/test_existence.rb +23 -0
  61. data/test/unit/svn/working_copy/test_externals.rb +56 -0
  62. data/test/unit/svn/working_copy/test_finalization.rb +17 -0
  63. data/test/unit/svn/working_copy/test_guessing.rb +18 -0
  64. data/test/unit/svn/working_copy/test_rememberance.rb +26 -0
  65. data/test/unit/test_info.rb +37 -0
  66. data/test/unit/test_lock_unlock.rb +47 -0
  67. data/test/unit/test_repository.rb +51 -0
  68. data/test/unit/test_revision.rb +31 -0
  69. data/test/unit/working_copy/test_guessing.rb +35 -0
  70. data/test/unit/working_copy/test_info.rb +14 -0
  71. data/test/unit/working_copy/test_rememberance.rb +42 -0
  72. data/test/unit/working_copy/test_validate.rb +63 -0
  73. metadata +132 -31
  74. data/CHANGELOG +0 -81
  75. data/LICENSE +0 -19
  76. data/Rakefile +0 -63
  77. data/contrib/piston +0 -43
  78. data/lib/core_ext/range.rb +0 -5
  79. data/lib/core_ext/string.rb +0 -9
  80. data/lib/piston/command.rb +0 -68
  81. data/lib/piston/command_error.rb +0 -6
  82. data/lib/piston/commands/lock.rb +0 -30
  83. data/lib/piston/commands/switch.rb +0 -139
  84. data/lib/piston/commands/unlock.rb +0 -29
  85. data/lib/transat/parser.rb +0 -189
@@ -0,0 +1,63 @@
1
+ require "piston/git/commit"
2
+ require "uri"
3
+
4
+ module Piston
5
+ module Git
6
+ class Repository < Piston::Repository
7
+ Piston::Repository.add_handler self
8
+
9
+ class << self
10
+ def understands_url?(url)
11
+ uri = URI.parse(url) rescue nil
12
+ return true if uri && %w(git).include?(uri.scheme)
13
+
14
+ begin
15
+ response = git("ls-remote", "--heads", url.sub(/\?.+$/, ""))
16
+ return false if response.nil? || response.strip.chomp.empty?
17
+ !!(response =~ /[a-f\d]{40}\s/)
18
+ rescue Piston::Git::Client::CommandError
19
+ false
20
+ end
21
+ end
22
+
23
+ def client
24
+ @@client ||= Piston::Git::Client.instance
25
+ end
26
+
27
+ def git(*args)
28
+ client.git(*args)
29
+ end
30
+
31
+ def repository_type
32
+ 'git'
33
+ end
34
+ end
35
+
36
+ attr_reader :branchname
37
+
38
+ def initialize(url)
39
+ @branchname = url.split("?")[1]
40
+ super(url.sub(/\?.+$/, ""))
41
+ end
42
+
43
+ def git(*args)
44
+ self.class.git(*args)
45
+ end
46
+
47
+ def at(commit)
48
+ case commit
49
+ when Hash
50
+ Piston::Git::Commit.new(self, commit[Piston::Git::COMMIT], commit)
51
+ when :head
52
+ Piston::Git::Commit.new(self, "HEAD")
53
+ else
54
+ Piston::Git::Commit.new(self, commit)
55
+ end
56
+ end
57
+
58
+ def basename
59
+ self.url.split("/").last.sub(".git", "")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,145 @@
1
+ require "piston/working_copy"
2
+ require "piston/git/client"
3
+
4
+ module Piston
5
+ module Git
6
+ class WorkingCopy < Piston::WorkingCopy
7
+ # Register ourselves as a handler for working copies
8
+ Piston::WorkingCopy.add_handler self
9
+
10
+ class << self
11
+ def understands_dir?(dir)
12
+ path = dir
13
+ begin
14
+ begin
15
+ logger.debug {"git status on #{path}"}
16
+ Dir.chdir(path) do
17
+ response = git(:status)
18
+ return true if response =~ /# On branch /
19
+ end
20
+ rescue Errno::ENOENT
21
+ # NOP, we assume this is simply because the folder hasn't been created yet
22
+ path = path.parent
23
+ retry unless path.to_s == "/"
24
+ return false
25
+ end
26
+ rescue Piston::Git::Client::BadCommand
27
+ # NOP, as we return false below
28
+ rescue Piston::Git::Client::CommandError
29
+ # This is certainly not a Git repository
30
+ false
31
+ end
32
+
33
+ false
34
+ end
35
+
36
+ def client
37
+ @@client ||= Piston::Git::Client.instance
38
+ end
39
+
40
+ def git(*args)
41
+ client.git(*args)
42
+ end
43
+ end
44
+
45
+ def git(*args)
46
+ self.class.git(*args)
47
+ end
48
+
49
+ def create
50
+ path.mkpath rescue nil
51
+ end
52
+
53
+ def exist?
54
+ path.directory?
55
+ end
56
+
57
+ def after_remember(path)
58
+ Dir.chdir(self.path) { git(:add, path.relative_path_from(self.path)) }
59
+ end
60
+
61
+ def finalize
62
+ Dir.chdir(path) { git(:add, ".") }
63
+ end
64
+
65
+ def add(added)
66
+ Dir.chdir(path) do
67
+ added.each do |item|
68
+ item.mkdir unless item.exist?
69
+ git(:add, item)
70
+ end
71
+ end
72
+ end
73
+
74
+ def delete(deleted)
75
+ Dir.chdir(path) do
76
+ deleted.each { |item| git(:rm, item) }
77
+ end
78
+ end
79
+
80
+ def rename(renamed)
81
+ Dir.chdir(path) do
82
+ renamed.each { |from, to| git(:mv, from, to) }
83
+ end
84
+ end
85
+
86
+ def downgrade_to(revision)
87
+ logger.debug {"Creating a branch to copy changes from remote repository"}
88
+ Dir.chdir(path) { git(:checkout, '-b', "my-#{revision}", revision) }
89
+ end
90
+
91
+ def merge_local_changes(revision)
92
+ from_revision = current_revision
93
+ Dir.chdir(path) do
94
+ begin
95
+ logger.debug {"Saving changes in temporary branch"}
96
+ git(:commit, '-a', '-m', 'merging')
97
+ logger.debug {"Return to previous branch"}
98
+ git(:checkout, revision)
99
+ logger.debug {"Merge changes from temporary branch"}
100
+ git(:merge, '--squash', from_revision)
101
+ rescue Piston::Git::Client::CommandError
102
+ git(:checkout, revision)
103
+ ensure
104
+ logger.debug {"Deleting temporary branch"}
105
+ git(:branch, '-D', from_revision)
106
+ end
107
+ end
108
+ end
109
+
110
+ def locally_modified
111
+ logger.debug {"Get last changed revision for #{yaml_path}"}
112
+ # get latest commit for .piston.yml
113
+ initial_revision = last_changed_revision(yaml_path)
114
+ logger.debug {"Get last log line for #{path} after #{initial_revision}"}
115
+ # get latest revisions for this working copy since last update
116
+ Dir.chdir(path) { not git(:log, '-n', '1', "#{initial_revision}..", '.').empty? }
117
+ end
118
+
119
+ def exclude_for_diff
120
+ Piston::Git::EXCLUDE
121
+ end
122
+
123
+ def status(subpath=nil)
124
+ Dir.chdir(path) do
125
+ git(:status).split("\n").inject([]) do |memo, line|
126
+ next memo unless line =~ /\s(\w+):\s+(.*)$/
127
+ memo << [$1, $2]
128
+ end
129
+ end
130
+ end
131
+
132
+ protected
133
+ def current_revision
134
+ Dir.chdir(path) { git(:branch).match(/^\*\s+(.+)$/)[1] }
135
+ end
136
+
137
+ def last_changed_revision(path)
138
+ path = Pathname.new(path) unless path.is_a? Pathname
139
+ path = path.relative_path_from(self.path) unless path.relative?
140
+ logger.debug {"Get last log line for #{path}"}
141
+ Dir.chdir(self.path) { git(:log, '-n', '1', path).match(/commit\s+(.*)$/)[1] }
142
+ end
143
+ end
144
+ end
145
+ end
data/lib/piston/git.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "piston/git/client"
2
+ require "piston/git/repository"
3
+ require "piston/git/commit"
4
+ require "piston/git/working_copy"
5
+
6
+ module Piston
7
+ module Git
8
+ URL = "url"
9
+ COMMIT = "commit"
10
+ BRANCH = "branch"
11
+ EXCLUDE = [".git"]
12
+ end
13
+ end
@@ -0,0 +1,61 @@
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
+ @url
51
+ end
52
+
53
+ def inspect
54
+ "Piston::Repository(#{@url})"
55
+ end
56
+
57
+ def ==(other)
58
+ url == other.url
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,83 @@
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, :dir
12
+
13
+ def initialize(repository, revision, recalled_values={})
14
+ @repository, @revision, @recalled_values = repository, revision, recalled_values
15
+ end
16
+
17
+ def url
18
+ repository.url
19
+ end
20
+
21
+ def name
22
+ @revision
23
+ end
24
+
25
+ def logger
26
+ self.class.logger
27
+ end
28
+
29
+ def to_s
30
+ "revision #{@revision}"
31
+ end
32
+
33
+ def inspect
34
+ "Piston::Revision(#{@repository.url}@#{@revision})"
35
+ end
36
+
37
+ # Retrieve a copy of this repository into +dir+.
38
+ def checkout_to(dir)
39
+ logger.debug {"Checking out #{@repository}@#{@revision} into #{dir}"}
40
+ @dir = dir.kind_of?(Pathname) ? dir : Pathname.new(dir)
41
+ end
42
+
43
+ # Update a copy of this repository to revision +to+.
44
+ def update_to(to, lock)
45
+ raise SubclassResponsibilityError, "Piston::Revision#update_to should be implemented by a subclass."
46
+ end
47
+
48
+ # What values does this revision want to remember for the future ?
49
+ def remember_values
50
+ logger.debug {"Generating remember values"}
51
+ {}
52
+ end
53
+
54
+ # Yields each file of this revision in turn to our caller.
55
+ def each
56
+ end
57
+
58
+ # Copies +relpath+ (relative to ourselves) to +abspath+ (an absolute path).
59
+ def copy_to(relpath, abspath)
60
+ raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @dir
61
+
62
+ Pathname.new(abspath).dirname.mkpath
63
+ FileUtils.cp(@dir + relpath, abspath)
64
+ end
65
+
66
+ # Copies +abspath+ (an absolute path) to +relpath+ (relative to ourselves).
67
+ def copy_from(abspath, relpath)
68
+ raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @dir
69
+
70
+ target = @dir + relpath
71
+ Pathname.new(target).dirname.mkpath
72
+ FileUtils.cp(abspath, target)
73
+ end
74
+
75
+ def remotely_modified
76
+ raise SubclassResponsibilityError, "Piston::Revision#remotely_modified should be implemented by a subclass."
77
+ end
78
+
79
+ def exclude_for_diff
80
+ raise SubclassResponsibilityError, "Piston::Revision#exclude_for_diff should be implemented by a subclass."
81
+ end
82
+ end
83
+ 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.to_s =~ /\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,112 @@
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(dir)
24
+ super
25
+ answer = svn(:checkout, "--revision", revision, repository.url, dir)
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 InvalidRevision, "Did not get the revision I wanted to checkout. Subversion checked out #{$1}, I wanted #{revision}"
31
+ end
32
+ else
33
+ raise InvalidRevision, "Could not checkout revision #{revision} from #{repository.url} to #{dir}\n#{answer}"
34
+ end
35
+ end
36
+
37
+ def update_to(revision)
38
+ raise ArgumentError, "Revision #{self.revision} of #{repository.url} was never checked out -- can't update" unless @dir
39
+
40
+ answer = svn(:update, "--non-interactive", "--revision", revision, @dir)
41
+ if answer =~ /(Updated to|At) revision (\d+)[.]/ then
42
+ if revision == "HEAD" then
43
+ @revision = $2.to_i
44
+ elsif revision != $2.to_i then
45
+ raise InvalidRevision, "Did not get the revision I wanted to update. Subversion update to #{$1}, I wanted #{revision}"
46
+ end
47
+ else
48
+ raise InvalidRevision, "Could not update #{@dir} to revision #{revision} from #{repository.url}\n#{answer}"
49
+ end
50
+ added = relative_paths(answer.scan(/^A\s+(.*)$/).flatten)
51
+ deleted = relative_paths(answer.scan(/^D\s+(.*)$/).flatten)
52
+ renamed = []
53
+ [added, deleted, renamed]
54
+ end
55
+
56
+ def remember_values
57
+ str = svn(:info, "--revision", revision, repository.url)
58
+ raise Failed, "Could not get 'svn info' from #{repository.url} at revision #{revision}" if str.nil? || str.chomp.strip.empty?
59
+ info = YAML.load(str)
60
+ { Piston::Svn::UUID => info["Repository UUID"],
61
+ Piston::Svn::REMOTE_REV => info["Revision"]}
62
+ end
63
+
64
+ def each
65
+ raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @dir
66
+
67
+ svn(:ls, "--recursive", @dir).split("\n").each do |relpath|
68
+ next if relpath =~ %r{/$}
69
+ yield relpath.chomp
70
+ end
71
+ end
72
+
73
+ def validate!
74
+ data = svn(:info, "--revision", revision, repository.url)
75
+ info = YAML.load(data)
76
+ actual_uuid = info["Repository UUID"]
77
+ raise RepositoryMoved, "Repository at #{repository.url} does not exist anymore:\n#{data}" if actual_uuid.blank?
78
+ raise UuidChanged, "Expected repository at #{repository.url} to have UUID #{recalled_uuid} but found #{actual_uuid}" if recalled_uuid != actual_uuid
79
+ end
80
+
81
+ def recalled_uuid
82
+ recalled_values[Piston::Svn::UUID]
83
+ end
84
+
85
+ def remotely_modified
86
+ logger.debug {"Get last revision in #{repository.url}"}
87
+ data = svn(:info, repository.url)
88
+ info = YAML.load(data)
89
+ latest_revision = info["Last Changed Rev"].to_i
90
+ revision < latest_revision
91
+ end
92
+
93
+ def exclude_for_diff
94
+ Piston::Svn::EXCLUDE
95
+ end
96
+
97
+ def resolve!
98
+ logger.debug {"Resolving #{@revision} to it's real value"}
99
+ return if @revision.to_i == @revision && !@revision.blank?
100
+ data = YAML.load(svn(:info, repository.url))
101
+ @revision = data["Last Changed Rev"].to_i
102
+ logger.debug {"Resolved #{@revision}"}
103
+ @revision
104
+ end
105
+
106
+ private
107
+ def relative_paths(paths)
108
+ paths.map { |item| Pathname.new(item).relative_path_from(@dir) }
109
+ end
110
+ end
111
+ end
112
+ end