piston 1.4.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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