scm 0.1.0.pre1

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/lib/scm/hg.rb ADDED
@@ -0,0 +1,469 @@
1
+ require 'scm/repository'
2
+ require 'scm/commits/hg'
3
+
4
+ require 'uri'
5
+
6
+ module SCM
7
+ #
8
+ # Interacts with Mercurial (Hg) repositories.
9
+ #
10
+ class Hg < Repository
11
+
12
+ # Hg status codes
13
+ STATUSES = {
14
+ 'M' => :modified,
15
+ 'A' => :added,
16
+ 'R' => :removed,
17
+ 'C' => :clean,
18
+ '!' => :missing,
19
+ '?' => :untracked,
20
+ 'I' => :ignored,
21
+ ' ' => :origin
22
+ }
23
+
24
+ #
25
+ # Creates a Hg repository.
26
+ #
27
+ # @param [String] path
28
+ # The path to the repository.
29
+ #
30
+ # @return [Hg, URI::Generic]
31
+ # The initialized local Hg repository or the URI to the remote
32
+ # repository.
33
+ #
34
+ # @raise [RuntimeError]
35
+ # Could not initialize the Hg repository.
36
+ #
37
+ def self.create(path,options={})
38
+ unless path.start_with?('ssh://')
39
+ FileUtils.mkdir_p(path)
40
+ end
41
+
42
+ unless (result = system('hg','init',path))
43
+ raise("unable to initialize Hg repository #{path.dump}")
44
+ end
45
+
46
+ if path.start_with?('ssh://')
47
+ return URI(path)
48
+ else
49
+ return new(path)
50
+ end
51
+ end
52
+
53
+ #
54
+ # Clones a remote Hg repository.
55
+ #
56
+ # @param [URI, String] uri
57
+ # The URI of the remote repository.
58
+ #
59
+ # @param [Hash] options
60
+ # Additional options.
61
+ #
62
+ # @option options [String, Integer] :commits
63
+ # The commits to include.
64
+ #
65
+ # @option options [String, Symbol] :branch
66
+ # The branch to specifically clone.
67
+ #
68
+ # @option options [String] :dest
69
+ # The destination directory to clone into.
70
+ #
71
+ # @return [Boolean]
72
+ # Specifies whether the clone was successful.
73
+ #
74
+ def self.clone(uri,options={})
75
+ arguments = []
76
+
77
+ if options[:commits]
78
+ arguments << '--rev' << options[:commits]
79
+ end
80
+
81
+ if options[:branch]
82
+ arguments << '--branch' << options[:branch]
83
+ end
84
+
85
+ arguments << uri
86
+ arguments << options[:dest] if options[:dest]
87
+
88
+ system('hg','clone',*arguments)
89
+ end
90
+
91
+ #
92
+ # Queries the status of the repository.
93
+ #
94
+ # @param [Array] paths
95
+ # Optional paths to query the statuses of.
96
+ #
97
+ # @return [Hash{String => Symbol}]
98
+ # The paths and their repsective statuses.
99
+ #
100
+ def status(*paths)
101
+ statuses = {}
102
+
103
+ popen('hg status',*paths) do |line|
104
+ status, path = line.split(' ',2)
105
+
106
+ statuses[path] = STATUSES[status]
107
+ end
108
+
109
+ return statuses
110
+ end
111
+
112
+ #
113
+ # Adds paths to the repository.
114
+ #
115
+ # @param [Array] paths
116
+ # The paths to add to the repository.
117
+ #
118
+ def add(*paths)
119
+ hg(:add,*paths)
120
+ end
121
+
122
+ #
123
+ # Moves a file or directory.
124
+ #
125
+ # @param [String] source
126
+ # The path of the source file/directory.
127
+ #
128
+ # @param [String] dest
129
+ # The new destination path.
130
+ #
131
+ # @param [Boolean] force
132
+ # Specifies whether to force the move.
133
+ #
134
+ def move(source,dest,force=false)
135
+ arguments = []
136
+
137
+ arguments << '--force' if force
138
+ arguments << source << dest
139
+
140
+ svn(:mv,*arguments)
141
+ end
142
+
143
+ #
144
+ # Removes files or directories.
145
+ #
146
+ # @param [String, Array] paths
147
+ # The path(s) to remove.
148
+ #
149
+ # @param [Hash] options
150
+ # Additional options.
151
+ #
152
+ # @option options [Boolean] :force (false)
153
+ # Specifies whether to forcibly remove the files/directories.
154
+ #
155
+ # @note
156
+ # {#remove} does not respond to the `:recursive` option.
157
+ # Hg removes directories recursively by default.
158
+ #
159
+ def remove(paths,options={})
160
+ arguments = []
161
+
162
+ arguments << '--force' if options[:force]
163
+ arguments += ['--', *paths]
164
+
165
+ hg(:rm,*arguments)
166
+ end
167
+
168
+ #
169
+ # Makes a Hg commit.
170
+ #
171
+ # @param [String] message
172
+ # The message for the commit.
173
+ #
174
+ # @param [Hash] options
175
+ # Commit options.
176
+ #
177
+ # @option options [String] :paths
178
+ # The path of the file to commit.
179
+ #
180
+ # @return [Boolean]
181
+ # Specifies whether the commit was successfully made.
182
+ #
183
+ def commit(message=nil,options={})
184
+ arguments = []
185
+
186
+ if message
187
+ arguments << '-m' << message
188
+ end
189
+
190
+ if options[:paths]
191
+ arguments += [*options[:paths]]
192
+ end
193
+
194
+ hg(:commit,*arguments)
195
+ end
196
+
197
+ #
198
+ # Lists branches in the SVN repository.
199
+ #
200
+ # @return [Array<String>]
201
+ # The branch names.
202
+ #
203
+ def branches
204
+ branches = []
205
+
206
+ popen('hg branches') do |line|
207
+ branches << line[2..-1]
208
+ end
209
+
210
+ return branches
211
+ end
212
+
213
+ #
214
+ # The current branch.
215
+ #
216
+ # @return [String]
217
+ # The name of the current branch.
218
+ #
219
+ def branch
220
+ popen('hg branch').chomp
221
+ end
222
+
223
+ #
224
+ # Swtiches to another Hg branch.
225
+ #
226
+ # @param [String, Symbol] name
227
+ # The name of the branch to switch to.
228
+ #
229
+ # @return [Boolean]
230
+ # Specifies whether the branch was successfully switched.
231
+ #
232
+ def switch_branch(name)
233
+ hg(:update,name)
234
+ end
235
+
236
+ #
237
+ # Deletes a branch.
238
+ #
239
+ # @param [String] name
240
+ # The name of the branch to delete.
241
+ #
242
+ # @return [Boolean]
243
+ # Specifies whether the branch was successfully deleted.
244
+ #
245
+ def delete_branch(name)
246
+ hg(:commit,'--close-branch','-m',"Closing #{name}")
247
+ end
248
+
249
+ #
250
+ # Lists Hg tags.
251
+ #
252
+ # @return [Array<String>]
253
+ # The tag names.
254
+ #
255
+ def tags
256
+ tags = []
257
+
258
+ popen('hg tags') do |line|
259
+ tags << line[2..-1]
260
+ end
261
+
262
+ return tags
263
+ end
264
+
265
+ #
266
+ # Creates a Hg tag.
267
+ #
268
+ # @param [String] name
269
+ # The name for the tag.
270
+ #
271
+ # @param [String] commit
272
+ # The commit to create the tag at.
273
+ #
274
+ # @return [Boolean]
275
+ # Specifies whether the tag was successfully created.
276
+ #
277
+ def tag(name,commit=nil)
278
+ arguments = []
279
+
280
+ if commit
281
+ arguments << '-r' << commit
282
+ end
283
+
284
+ hg(:tag,name,*arguments)
285
+ end
286
+
287
+ #
288
+ # Deletes a Hg tag.
289
+ #
290
+ # @param [String] name
291
+ # The name of the tag.
292
+ #
293
+ # @return [Boolean]
294
+ # Specifies whether the tag was successfully deleted.
295
+ #
296
+ def delete_tag(name)
297
+ hg(:tag,'--remove',name)
298
+ end
299
+
300
+ #
301
+ # Prints the Hg log.
302
+ #
303
+ # @param [String] :commit
304
+ # Commit to begin the log at.
305
+ #
306
+ # @param [String] :paths
307
+ # File to list commits for.
308
+ #
309
+ def log(options={})
310
+ arguments = []
311
+
312
+ if options[:commit]
313
+ arguments << '-r' << options[:commit]
314
+ end
315
+
316
+ if options[:paths]
317
+ arguments += [*options[:paths]]
318
+ end
319
+
320
+ hg(:log,*arguments)
321
+ end
322
+
323
+ #
324
+ # Pushes changes to the remote Hg repository.
325
+ #
326
+ # @param [Hash] options
327
+ # Additional options.
328
+ #
329
+ # @option options [Boolean] :force
330
+ # Specifies whether to force pushing the changes.
331
+ #
332
+ # @option options [String, Symbol] :repository
333
+ # The remote repository to push to.
334
+ #
335
+ # @return [Boolean]
336
+ # Specifies whether the changes were successfully pushed.
337
+ #
338
+ def push(options={})
339
+ arguments = []
340
+
341
+ arguments << '-f' if options[:force]
342
+ arguments << options[:repository] if options[:repository]
343
+
344
+ hg(:push,*arguments)
345
+ end
346
+
347
+ #
348
+ # Pulls changes from the remote Hg repository.
349
+ #
350
+ # @param [Hash] options
351
+ # Additional options.
352
+ #
353
+ # @option options [Boolean] :force
354
+ # Specifies whether to force pushing the changes.
355
+ #
356
+ # @option options [String, Symbol] :repository
357
+ # The remote repository to push to.
358
+ #
359
+ # @return [Boolean]
360
+ # Specifies whether the changes were successfully pulled.
361
+ #
362
+ def pull(options={})
363
+ arguments = []
364
+
365
+ arguments << '-f' if options[:force]
366
+ arguments << options[:repository] if options[:repository]
367
+
368
+ hg(:pull,*arguments)
369
+ end
370
+
371
+ #
372
+ # Lists the commits in the Hg repository.
373
+ #
374
+ # @param [Hash] options
375
+ # Additional options.
376
+ #
377
+ # @option options [String] :commit
378
+ # Commit to start at.
379
+ #
380
+ # @option options [Symbol, String] :branch
381
+ # The branch to list commits within.
382
+ #
383
+ # @option options [Integer] :limit
384
+ # The number of commits to list.
385
+ #
386
+ # @option options [String, Array<String>] :paths
387
+ # The path(s) to list commits for.
388
+ #
389
+ # @yield [commit]
390
+ # The given block will be passed each commit.
391
+ #
392
+ # @yieldparam [Commits::Hg] commit
393
+ # A commit from the repository.
394
+ #
395
+ # @return [Enumerator<Commits::Hg>]
396
+ # The commits in the repository.
397
+ #
398
+ def commits(options={})
399
+ return enum_for(:commits,options) unless block_given?
400
+
401
+ arguments = []
402
+
403
+ if options[:commit]
404
+ arguments << '--rev' << options[:commit]
405
+ end
406
+
407
+ if options[:branch]
408
+ arguments << '--branch' << options[:branch]
409
+ end
410
+
411
+ if options[:limit]
412
+ arguments << '--limit' << options[:limit]
413
+ end
414
+
415
+ if options[:paths]
416
+ arguments.push(*options[:paths])
417
+ end
418
+
419
+ revision = nil
420
+ hash = nil
421
+ branch = nil
422
+ user = nil
423
+ date = nil
424
+ summary = nil
425
+
426
+ popen('hg log',*arguments) do |line|
427
+ if line.empty?
428
+ yield Commits::Hg.new(revision,hash,branch,user,date,summary)
429
+
430
+ revision = hash = branch = user = date = summary = nil
431
+ else
432
+ key, value = line.split(' ',2)
433
+
434
+ case key
435
+ when 'changeset:'
436
+ revision, hash = value.split(':',2)
437
+ when 'branch:'
438
+ branch = value
439
+ when 'user:'
440
+ user = value
441
+ when 'date:'
442
+ date = Time.parse(value)
443
+ when 'summary:'
444
+ summary = value
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ protected
451
+
452
+ #
453
+ # Runs a Hg command.
454
+ #
455
+ # @param [Symbol] command
456
+ # The Hg command to run.
457
+ #
458
+ # @param [Array] arguments
459
+ # Additional arguments to pass to the Hg command.
460
+ #
461
+ # @return [Boolean]
462
+ # Specifies whether the Hg command exited successfully.
463
+ #
464
+ def hg(command,*arguments)
465
+ run(:hg,command,*arguments)
466
+ end
467
+
468
+ end
469
+ end