scm 0.1.0.pre1

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