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