minmb-capistrano 2.15.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +7 -0
  3. data/CHANGELOG +1170 -0
  4. data/Gemfile +13 -0
  5. data/README.md +94 -0
  6. data/Rakefile +11 -0
  7. data/bin/cap +4 -0
  8. data/bin/capify +92 -0
  9. data/capistrano.gemspec +40 -0
  10. data/lib/capistrano.rb +5 -0
  11. data/lib/capistrano/callback.rb +45 -0
  12. data/lib/capistrano/cli.rb +47 -0
  13. data/lib/capistrano/cli/execute.rb +85 -0
  14. data/lib/capistrano/cli/help.rb +125 -0
  15. data/lib/capistrano/cli/help.txt +81 -0
  16. data/lib/capistrano/cli/options.rb +243 -0
  17. data/lib/capistrano/cli/ui.rb +40 -0
  18. data/lib/capistrano/command.rb +303 -0
  19. data/lib/capistrano/configuration.rb +57 -0
  20. data/lib/capistrano/configuration/actions/file_transfer.rb +50 -0
  21. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  22. data/lib/capistrano/configuration/actions/invocation.rb +329 -0
  23. data/lib/capistrano/configuration/alias_task.rb +26 -0
  24. data/lib/capistrano/configuration/callbacks.rb +147 -0
  25. data/lib/capistrano/configuration/connections.rb +237 -0
  26. data/lib/capistrano/configuration/execution.rb +142 -0
  27. data/lib/capistrano/configuration/loading.rb +205 -0
  28. data/lib/capistrano/configuration/log_formatters.rb +75 -0
  29. data/lib/capistrano/configuration/namespaces.rb +223 -0
  30. data/lib/capistrano/configuration/roles.rb +77 -0
  31. data/lib/capistrano/configuration/servers.rb +116 -0
  32. data/lib/capistrano/configuration/variables.rb +127 -0
  33. data/lib/capistrano/errors.rb +19 -0
  34. data/lib/capistrano/ext/multistage.rb +64 -0
  35. data/lib/capistrano/ext/string.rb +5 -0
  36. data/lib/capistrano/extensions.rb +57 -0
  37. data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
  38. data/lib/capistrano/logger.rb +166 -0
  39. data/lib/capistrano/processable.rb +57 -0
  40. data/lib/capistrano/recipes/compat.rb +32 -0
  41. data/lib/capistrano/recipes/deploy.rb +625 -0
  42. data/lib/capistrano/recipes/deploy/assets.rb +201 -0
  43. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  44. data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
  45. data/lib/capistrano/recipes/deploy/remote_dependency.rb +117 -0
  46. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  47. data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
  48. data/lib/capistrano/recipes/deploy/scm/base.rb +200 -0
  49. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  50. data/lib/capistrano/recipes/deploy/scm/cvs.rb +153 -0
  51. data/lib/capistrano/recipes/deploy/scm/darcs.rb +96 -0
  52. data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
  53. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
  54. data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
  55. data/lib/capistrano/recipes/deploy/scm/perforce.rb +152 -0
  56. data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
  57. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  58. data/lib/capistrano/recipes/deploy/strategy/base.rb +92 -0
  59. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  60. data/lib/capistrano/recipes/deploy/strategy/copy.rb +338 -0
  61. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  62. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  63. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +57 -0
  64. data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
  65. data/lib/capistrano/recipes/standard.rb +37 -0
  66. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  67. data/lib/capistrano/role.rb +102 -0
  68. data/lib/capistrano/server_definition.rb +56 -0
  69. data/lib/capistrano/shell.rb +265 -0
  70. data/lib/capistrano/ssh.rb +95 -0
  71. data/lib/capistrano/task_definition.rb +77 -0
  72. data/lib/capistrano/transfer.rb +218 -0
  73. data/lib/capistrano/version.rb +11 -0
  74. data/test/cli/execute_test.rb +132 -0
  75. data/test/cli/help_test.rb +165 -0
  76. data/test/cli/options_test.rb +329 -0
  77. data/test/cli/ui_test.rb +28 -0
  78. data/test/cli_test.rb +17 -0
  79. data/test/command_test.rb +322 -0
  80. data/test/configuration/actions/file_transfer_test.rb +61 -0
  81. data/test/configuration/actions/inspect_test.rb +76 -0
  82. data/test/configuration/actions/invocation_test.rb +288 -0
  83. data/test/configuration/alias_task_test.rb +118 -0
  84. data/test/configuration/callbacks_test.rb +201 -0
  85. data/test/configuration/connections_test.rb +439 -0
  86. data/test/configuration/execution_test.rb +175 -0
  87. data/test/configuration/loading_test.rb +148 -0
  88. data/test/configuration/namespace_dsl_test.rb +332 -0
  89. data/test/configuration/roles_test.rb +157 -0
  90. data/test/configuration/servers_test.rb +183 -0
  91. data/test/configuration/variables_test.rb +190 -0
  92. data/test/configuration_test.rb +77 -0
  93. data/test/deploy/local_dependency_test.rb +76 -0
  94. data/test/deploy/remote_dependency_test.rb +146 -0
  95. data/test/deploy/scm/accurev_test.rb +23 -0
  96. data/test/deploy/scm/base_test.rb +55 -0
  97. data/test/deploy/scm/bzr_test.rb +51 -0
  98. data/test/deploy/scm/darcs_test.rb +37 -0
  99. data/test/deploy/scm/git_test.rb +221 -0
  100. data/test/deploy/scm/mercurial_test.rb +134 -0
  101. data/test/deploy/scm/none_test.rb +35 -0
  102. data/test/deploy/scm/perforce_test.rb +23 -0
  103. data/test/deploy/scm/subversion_test.rb +40 -0
  104. data/test/deploy/strategy/copy_test.rb +360 -0
  105. data/test/extensions_test.rb +69 -0
  106. data/test/fixtures/cli_integration.rb +5 -0
  107. data/test/fixtures/config.rb +5 -0
  108. data/test/fixtures/custom.rb +3 -0
  109. data/test/logger_formatting_test.rb +149 -0
  110. data/test/logger_test.rb +134 -0
  111. data/test/recipes_test.rb +25 -0
  112. data/test/role_test.rb +11 -0
  113. data/test/server_definition_test.rb +121 -0
  114. data/test/shell_test.rb +96 -0
  115. data/test/ssh_test.rb +113 -0
  116. data/test/task_definition_test.rb +117 -0
  117. data/test/transfer_test.rb +168 -0
  118. data/test/utils.rb +37 -0
  119. metadata +316 -0
@@ -0,0 +1,200 @@
1
+ module Capistrano
2
+ module Deploy
3
+ module SCM
4
+
5
+ # The ancestor class for all Capistrano SCM implementations. It provides
6
+ # minimal infrastructure for subclasses to build upon and override.
7
+ #
8
+ # Note that subclasses that implement this abstract class only return
9
+ # the commands that need to be executed--they do not execute the commands
10
+ # themselves. In this way, the deployment method may execute the commands
11
+ # either locally or remotely, as necessary.
12
+ class Base
13
+ class << self
14
+ # If no parameters are given, it returns the current configured
15
+ # name of the command-line utility of this SCM. If a parameter is
16
+ # given, the defeault command is set to that value.
17
+ def default_command(value=nil)
18
+ if value
19
+ @default_command = value
20
+ else
21
+ @default_command
22
+ end
23
+ end
24
+ end
25
+
26
+ # Wraps an SCM instance and forces all messages sent to it to be
27
+ # relayed to the underlying SCM instance, in "local" mode. See
28
+ # Base#local.
29
+ class LocalProxy
30
+ def initialize(scm)
31
+ @scm = scm
32
+ end
33
+
34
+ def method_missing(sym, *args, &block)
35
+ @scm.local { return @scm.send(sym, *args, &block) }
36
+ end
37
+ end
38
+
39
+ # The options available for this SCM instance to reference. Should be
40
+ # treated like a hash.
41
+ attr_reader :configuration
42
+
43
+ # Creates a new SCM instance with the given configuration options.
44
+ def initialize(configuration={})
45
+ @configuration = configuration
46
+ end
47
+
48
+ # Returns a proxy that wraps the SCM instance and forces it to operate
49
+ # in "local" mode, which changes how variables are looked up in the
50
+ # configuration. Normally, if the value of a variable "foo" is needed,
51
+ # it is queried for in the configuration as "foo". However, in "local"
52
+ # mode, first "local_foo" would be looked for, and only if it is not
53
+ # found would "foo" be used. This allows for both (e.g.) "scm_command"
54
+ # and "local_scm_command" to be set, if the two differ.
55
+ #
56
+ # Alternatively, it may be called with a block, and for the duration of
57
+ # the block, all requests on this configuration object will be
58
+ # considered local.
59
+ def local
60
+ if block_given?
61
+ begin
62
+ saved, @local_mode = @local_mode, true
63
+ yield
64
+ ensure
65
+ @local_mode = saved
66
+ end
67
+ else
68
+ LocalProxy.new(self)
69
+ end
70
+ end
71
+
72
+ # Returns true if running in "local" mode. See #local.
73
+ def local?
74
+ @local_mode
75
+ end
76
+
77
+ # Returns the string used to identify the latest revision in the
78
+ # repository. This will be passed as the "revision" parameter of
79
+ # the methods below.
80
+ def head
81
+ raise NotImplementedError, "`head' is not implemented by #{self.class.name}"
82
+ end
83
+
84
+ # Checkout a copy of the repository, at the given +revision+, to the
85
+ # given +destination+. The checkout is suitable for doing development
86
+ # work in, e.g. allowing subsequent commits and updates.
87
+ def checkout(revision, destination)
88
+ raise NotImplementedError, "`checkout' is not implemented by #{self.class.name}"
89
+ end
90
+
91
+ # Resynchronize the working copy in +destination+ to the specified
92
+ # +revision+.
93
+ def sync(revision, destination)
94
+ raise NotImplementedError, "`sync' is not implemented by #{self.class.name}"
95
+ end
96
+
97
+ # Compute the difference between the two revisions, +from+ and +to+.
98
+ def diff(from, to=nil)
99
+ raise NotImplementedError, "`diff' is not implemented by #{self.class.name}"
100
+ end
101
+
102
+ # Return a log of all changes between the two specified revisions,
103
+ # +from+ and +to+, inclusive.
104
+ def log(from, to=nil)
105
+ raise NotImplementedError, "`log' is not implemented by #{self.class.name}"
106
+ end
107
+
108
+ # If the given revision represents a "real" revision, this should
109
+ # simply return the revision value. If it represends a pseudo-revision
110
+ # (like Subversions "HEAD" identifier), it should yield a string
111
+ # containing the commands that, when executed will return a string
112
+ # that this method can then extract the real revision from.
113
+ def query_revision(revision)
114
+ raise NotImplementedError, "`query_revision' is not implemented by #{self.class.name}"
115
+ end
116
+
117
+ # Returns the revision number immediately following revision, if at
118
+ # all possible. A block should always be passed to this method, which
119
+ # accepts a command to invoke and returns the result, although a
120
+ # particular SCM's implementation is not required to invoke the block.
121
+ #
122
+ # By default, this method simply returns the revision itself. If a
123
+ # particular SCM is able to determine a subsequent revision given a
124
+ # revision identifier, it should override this method.
125
+ def next_revision(revision)
126
+ revision
127
+ end
128
+
129
+ # Should analyze the given text and determine whether or not a
130
+ # response is expected, and if so, return the appropriate response.
131
+ # If no response is expected, return nil. The +state+ parameter is a
132
+ # hash that may be used to preserve state between calls. This method
133
+ # is used to define how Capistrano should respond to common prompts
134
+ # and messages from the SCM, like password prompts and such. By
135
+ # default, the output is simply displayed.
136
+ def handle_data(state, stream, text)
137
+ logger.info "[#{stream}] #{text}"
138
+ nil
139
+ end
140
+
141
+ # Returns the name of the command-line utility for this SCM. It first
142
+ # looks at the :scm_command variable, and if it does not exist, it
143
+ # then falls back to whatever was defined by +default_command+.
144
+ #
145
+ # If scm_command is set to :default, the default_command will be
146
+ # returned.
147
+ def command
148
+ command = variable(:scm_command)
149
+ command = nil if command == :default
150
+ command || default_command
151
+ end
152
+
153
+ # A helper method that can be used to define SCM commands naturally.
154
+ # It returns a single string with all arguments joined by spaces,
155
+ # with the scm command prefixed onto it.
156
+ def scm(*args)
157
+ [command, *args].compact.join(" ")
158
+ end
159
+
160
+ private
161
+
162
+ # A helper for accessing variable values, which takes into
163
+ # consideration the current mode ("normal" vs. "local").
164
+ def variable(name, default = nil)
165
+ if local? && configuration.exists?("local_#{name}".to_sym)
166
+ return configuration["local_#{name}".to_sym].nil? ? default : configuration["local_#{name}".to_sym]
167
+ else
168
+ configuration[name].nil? ? default : configuration[name]
169
+ end
170
+ end
171
+
172
+ # A reference to a Logger instance that the SCM can use to log
173
+ # activity.
174
+ def logger
175
+ @logger ||= variable(:logger) || Capistrano::Logger.new(:output => STDOUT)
176
+ end
177
+
178
+ # A helper for accessing the default command name for this SCM. It
179
+ # simply delegates to the class' +default_command+ method.
180
+ def default_command
181
+ self.class.default_command
182
+ end
183
+
184
+ # A convenience method for accessing the declared repository value.
185
+ def repository
186
+ variable(:repository)
187
+ end
188
+
189
+ def arguments(command = :all)
190
+ value = variable(:scm_arguments)
191
+ if value.is_a?(Hash)
192
+ value = value[command]
193
+ end
194
+ value
195
+ end
196
+ end
197
+
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,86 @@
1
+ require 'capistrano/recipes/deploy/scm/base'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module SCM
6
+
7
+ # Implements the Capistrano SCM interface for the Bazaar-NG revision
8
+ # control system (http://bazaar-vcs.org/).
9
+ class Bzr < Base
10
+ # Sets the default command name for this SCM. Users may override this
11
+ # by setting the :scm_command variable.
12
+ default_command "bzr"
13
+
14
+ # Bazaar-NG doesn't support any pseudo-id's, so we'll use the convention
15
+ # in this adapter that the :head symbol means the most recently
16
+ # committed revision.
17
+ def head
18
+ :head
19
+ end
20
+
21
+ # Returns the command that will check out the given revision to the
22
+ # given destination.
23
+ def checkout(revision, destination)
24
+ scm :checkout, "--lightweight", revswitch(revision), repository, destination
25
+ end
26
+
27
+ # The bzr 'update' command does not support updating to a specific
28
+ # revision, so this just does update, followed by revert (unless
29
+ # updating to head).
30
+ def sync(revision, destination)
31
+ commands = [scm(:update, destination)]
32
+ commands << [scm(:revert, revswitch(revision), destination)] if revision != head
33
+ commands.join(" && ")
34
+ end
35
+
36
+ # The bzr 'export' does an export similar to other SCM systems
37
+ def export(revision, destination)
38
+ scm :export, revswitch(revision), destination, repository
39
+ end
40
+
41
+ # The bzr "diff" command doesn't accept a repository argument, so it
42
+ # must be run from within a working tree.
43
+ def diff(from, to=nil)
44
+ switch = "-r#{from}"
45
+ switch << "..#{to}" if to
46
+
47
+ scm :diff, switch
48
+ end
49
+
50
+ # Returns a log of changes between the two revisions (inclusive).
51
+ def log(from, to=nil)
52
+ scm :log, "--short", "-r#{from}..#{to}", repository
53
+ end
54
+
55
+ # Attempts to translate the given revision identifier to a "real"
56
+ # revision. If the identifier is :head, the "bzr revno" command will
57
+ # be yielded, and the block must execute the command and return the
58
+ # output. The revision will be extracted from the output and returned.
59
+ # If the 'revision' argument, on the other hand, is not :head, it is
60
+ # simply returned.
61
+ def query_revision(revision)
62
+ return revision unless :head == revision
63
+
64
+ command = scm('revno', repository)
65
+ result = yield(command)
66
+ end
67
+
68
+ # Increments the given revision number and returns it.
69
+ def next_revision(revision)
70
+ revision.to_i + 1
71
+ end
72
+
73
+ private
74
+
75
+ def revswitch(revision)
76
+ if revision == :head || revision.nil?
77
+ nil
78
+ else
79
+ "-r #{revision}".chomp
80
+ end
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,153 @@
1
+ require 'capistrano/recipes/deploy/scm/base'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module SCM
6
+
7
+ # Implements the Capistrano SCM interface for the CVS revision
8
+ # control system.
9
+ class Cvs < Base
10
+ # Sets the default command name for this SCM. Users may override this
11
+ # by setting the :scm_command variable.
12
+ default_command "cvs"
13
+
14
+ # CVS understands 'HEAD' to refer to the latest revision in the
15
+ # repository.
16
+ def head
17
+ "HEAD"
18
+ end
19
+
20
+ # Returns the command that will check out the given revision to the
21
+ # given destination.
22
+ def checkout(revision, destination)
23
+ [ prep_destination(destination),
24
+ scm(verbose, cvs_root, :checkout, cvs_revision(revision), cvs_destination(destination), variable(:scm_module))
25
+ ].join(' && ')
26
+ end
27
+
28
+ # Returns the command that will do an "cvs update" to the given
29
+ # revision, for the working copy at the given destination.
30
+ def sync(revision, destination)
31
+ [ prep_destination(destination),
32
+ scm(verbose, cvs_root, :update, cvs_revision(revision), cvs_destination(destination))
33
+ ].join(' && ')
34
+ end
35
+
36
+ # Returns the command that will do an "cvs export" of the given revision
37
+ # to the given destination.
38
+ def export(revision, destination)
39
+ [ prep_destination(destination),
40
+ scm(verbose, cvs_root, :export, cvs_revision(revision), cvs_destination(destination), variable(:scm_module))
41
+ ].join(' && ')
42
+ end
43
+
44
+ # Returns the command that will do an "cvs diff" for the two revisions.
45
+ def diff(from, to=nil)
46
+ rev_type = revision_type(from)
47
+ if rev_type == :date
48
+ range_args = "-D '#{from}' -D '#{to || 'now'}'"
49
+ else
50
+ range_args = "-r '#{from}' -r '#{to || head}'"
51
+ end
52
+ scm cvs_root, :diff, range_args
53
+ end
54
+
55
+ # Returns an "cvs log" command for the two revisions.
56
+ def log(from, to=nil)
57
+ rev_type = revision_type(from)
58
+ if rev_type == :date
59
+ range_arg = "-d '#{from}<#{to || 'now'}'"
60
+ else
61
+ range_arg = "-r '#{from}:#{to || head}'"
62
+ end
63
+ scm cvs_root, :log, range_arg
64
+ end
65
+
66
+ # Unfortunately, cvs doesn't support the concept of a revision number like
67
+ # subversion and other SCM's do. For now, we'll rely on getting the timestamp
68
+ # of the latest checkin under the revision that's passed to us.
69
+ def query_revision(revision)
70
+ return revision if revision_type(revision) == :date
71
+ revision = yield(scm(cvs_root, :log, "-r#{revision}")).
72
+ split("\n").
73
+ select { |line| line =~ /^date:/ }.
74
+ map { |line| line[/^date: (.*?);/, 1] }.
75
+ sort.last + " UTC"
76
+ return revision
77
+ end
78
+
79
+ # Determines what the response should be for a particular bit of text
80
+ # from the SCM. Password prompts, connection requests, passphrases,
81
+ # etc. are handled here.
82
+ def handle_data(state, stream, text)
83
+ logger.info "[#{stream}] #{text}"
84
+ case text
85
+ when /\bpassword.*:/i
86
+ # prompting for a password
87
+ "#{variable(:scm_password) || variable(:password)}\n"
88
+ when %r{\(yes/no\)}
89
+ # let's be agreeable...
90
+ "yes\n"
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # Constructs the CVSROOT command-line option
97
+ def cvs_root
98
+ root = ""
99
+ root << "-d #{repository} " if repository
100
+ root
101
+ end
102
+
103
+ # Constructs the destination dir command-line option
104
+ def cvs_destination(destination)
105
+ dest = ""
106
+ if destination
107
+ dest_parts = destination.split(/\//);
108
+ dest << "-d #{dest_parts.pop}"
109
+ end
110
+ dest
111
+ end
112
+
113
+ # attempts to guess what type of revision we're working with
114
+ def revision_type(rev)
115
+ return :date if rev =~ /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2} UTC$/ # i.e 2007-05-15 08:13:25 UTC
116
+ return :date if rev =~ /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}$/ # i.e 2007-05-15 08:13:25
117
+ return :revision if rev =~ /^\d/ # i.e. 1.2.1
118
+ return :tag # i.e. RELEASE_1_2
119
+ end
120
+
121
+ # constructs the appropriate command-line switch for specifying a
122
+ # "revision" in CVS. This could be a tag, branch, revision (i.e. 1.3)
123
+ # or a date (to be used with -d)
124
+ def cvs_revision(rev)
125
+ revision = ""
126
+ revision << case revision_type(rev)
127
+ when :date
128
+ "-D \"#{rev}\"" if revision_type(rev) == :date
129
+ when :revision
130
+ "-r #{rev}"
131
+ else
132
+ "-r #{head}"
133
+ end
134
+ return revision
135
+ end
136
+
137
+ # If verbose output is requested, return nil, otherwise return the
138
+ # command-line switch for "quiet" ("-Q").
139
+ def verbose
140
+ variable(:scm_verbose) ? nil : "-Q"
141
+ end
142
+
143
+ def prep_destination(destination)
144
+ dest_parts = destination.split(/\//);
145
+ checkout_dir = dest_parts.pop
146
+ dest = dest_parts.join('/')
147
+ "mkdir -p #{ dest } && cd #{ dest }"
148
+ end
149
+ end
150
+
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,96 @@
1
+ require 'capistrano/recipes/deploy/scm/base'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module SCM
6
+
7
+ # Implements the Capistrano SCM interface for the darcs revision
8
+ # control system (http://www.abridgegame.org/darcs/).
9
+ class Darcs < Base
10
+ # Sets the default command name for this SCM. Users may override this
11
+ # by setting the :scm_command variable.
12
+ default_command "darcs"
13
+
14
+ # Because darcs does not have any support for pseudo-ids, we'll just
15
+ # return something here that we can use in the helpers below for
16
+ # determining whether we need to look up the latest revision.
17
+ def head
18
+ :head
19
+ end
20
+
21
+ def to_match(revision)
22
+ if revision.nil? || revision == self.head
23
+ nil
24
+ else
25
+ "--to-match='hash #{revision}'"
26
+ end
27
+ end
28
+
29
+ # Returns the command that will check out the given revision to the
30
+ # given destination. The 'revision' parameter must be the 'hash' value
31
+ # for the revision in question, as given by 'darcs changes --xml-output'.
32
+ def checkout(revision, destination)
33
+ scm :get, *[verbose,
34
+ "--repo-name=#{destination}",
35
+ to_match(revision),
36
+ repository].compact
37
+ end
38
+
39
+ # Tries to update the destination repository in-place, to bring it up
40
+ # to the given revision. Note that because darcs' "pull" operation
41
+ # does not support a "to-match" argument (or similar), this basically
42
+ # nukes the destination directory and re-gets it.
43
+ def sync(revision, destination)
44
+ ["rm -rf #{destination}", checkout(revision, destination)].join(" && ")
45
+ end
46
+
47
+ # Darcs does not have a real 'export' option; there is 'darcs dist',
48
+ # but that presupposes a utility that can untar and ungzip the dist
49
+ # file. We'll cheat and just do a get, followed by a deletion of the
50
+ # _darcs metadata directory.
51
+ def export(revision, destination)
52
+ [checkout(revision, destination), "rm -rf #{destination}/_darcs"].join(" && ")
53
+ end
54
+
55
+ # Returns the command that will do a "darcs diff" for the two revisions.
56
+ # Each revision must be the 'hash' identifier of a darcs revision.
57
+ def diff(from, to=nil)
58
+ scm :diff, "--from-match 'hash #{from}'", to && "--to-match 'hash #{to}'"
59
+ end
60
+
61
+ # Returns the log of changes between the two revisions. Each revision
62
+ # must be the 'hash' identifier of a darcs revision.
63
+ def log(from, to=nil)
64
+ scm :changes, "--from-match 'hash #{from}'", to && "--to-match 'hash #{to}'", "--repo=#{repository}"
65
+ end
66
+
67
+ # Attempts to translate the given revision identifier to a "real"
68
+ # revision. If the identifier is a symbol, it is assumed to be a
69
+ # pseudo-id. Otherwise, it will be immediately returned. If it is a
70
+ # pseudo-id, a set of commands to execute will be yielded, and the
71
+ # result of executing those commands must be returned by the block.
72
+ # This method will then extract the actual revision hash from the
73
+ # returned data.
74
+ def query_revision(revision)
75
+ case revision
76
+ when :head
77
+ xml = yield(scm(:changes, "--last 1", "--xml-output", "--repo=#{repository}"))
78
+ return xml[/hash='(.*?)'/, 1]
79
+ else return revision
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def verbose
86
+ case variable(:scm_verbose)
87
+ when nil then "-q"
88
+ when false then nil
89
+ else "-v"
90
+ end
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ end