ruby-git-lacravate 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/git/lib.rb ADDED
@@ -0,0 +1,765 @@
1
+ require 'tempfile'
2
+
3
+ module Git
4
+
5
+ class GitExecuteError < StandardError
6
+ end
7
+
8
+ class Lib
9
+
10
+ def initialize(base = nil, logger = nil)
11
+ @git_dir = nil
12
+ @git_index_file = nil
13
+ @git_work_dir = nil
14
+ @path = nil
15
+
16
+ if base.is_a?(Git::Base)
17
+ @git_dir = base.repo.path
18
+ @git_index_file = base.index.path if base.index
19
+ @git_work_dir = base.dir.path if base.dir
20
+ elsif base.is_a?(Hash)
21
+ @git_dir = base[:repository]
22
+ @git_index_file = base[:index]
23
+ @git_work_dir = base[:working_directory]
24
+ end
25
+ @logger = logger
26
+ end
27
+
28
+ def init
29
+ command('init')
30
+ end
31
+
32
+ # tries to clone the given repo
33
+ #
34
+ # returns {:repository} (if bare)
35
+ # {:working_directory} otherwise
36
+ #
37
+ # accepts options:
38
+ # :remote:: name of remote (rather than 'origin')
39
+ # :bare:: no working directory
40
+ # :depth:: the number of commits back to pull
41
+ #
42
+ # TODO - make this work with SSH password or auth_key
43
+ #
44
+ def clone(repository, name, opts = {})
45
+ @path = opts[:path] || '.'
46
+ clone_dir = opts[:path] ? File.join(@path, name) : name
47
+
48
+ arr_opts = []
49
+ arr_opts << "--bare" if opts[:bare]
50
+ arr_opts << "-o" << opts[:remote] if opts[:remote]
51
+ arr_opts << "--depth" << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0
52
+
53
+ # -- allow processing of switches added as array elements
54
+ Array(opts[:switches]).each { |switch| arr_opts << switch }
55
+ #
56
+
57
+ arr_opts << '--'
58
+ arr_opts << repository
59
+ arr_opts << clone_dir
60
+
61
+ command('clone', arr_opts)
62
+
63
+ opts[:bare] ? {:repository => clone_dir} : {:working_directory => clone_dir}
64
+ end
65
+
66
+
67
+ ## READ COMMANDS ##
68
+
69
+
70
+ def log_commits(opts = {})
71
+ arr_opts = ['--pretty=oneline']
72
+ arr_opts << "-#{opts[:count]}" if opts[:count]
73
+ arr_opts << "--since=#{opts[:since]}" if opts[:since].is_a? String
74
+ arr_opts << "--until=#{opts[:until]}" if opts[:until].is_a? String
75
+ arr_opts << "--grep=#{opts[:grep]}" if opts[:grep].is_a? String
76
+ arr_opts << "--author=#{opts[:author]}" if opts[:author].is_a? String
77
+ arr_opts << "#{opts[:between][0].to_s}..#{opts[:between][1].to_s}" if (opts[:between] && opts[:between].size == 2)
78
+ arr_opts << opts[:object] if opts[:object].is_a? String
79
+
80
+ # -- always add '--' at the end of command line to allow git to
81
+ # understand commands like 'git log `branch`'
82
+ arr_opts << '--'
83
+ arr_opts << opts[:path_limiter] if opts[:path_limiter].is_a? String
84
+ #
85
+
86
+ command_lines('log', arr_opts, true).map { |l| l.split.first }
87
+ end
88
+
89
+ def full_log_commits(opts = {})
90
+ arr_opts = ['--pretty=raw']
91
+ arr_opts << "-#{opts[:count]}" if opts[:count]
92
+ arr_opts << "--skip=#{opts[:skip]}" if opts[:skip]
93
+ arr_opts << "--since=#{opts[:since]}" if opts[:since].is_a? String
94
+ arr_opts << "--until=#{opts[:until]}" if opts[:until].is_a? String
95
+ arr_opts << "--grep=#{opts[:grep]}" if opts[:grep].is_a? String
96
+ arr_opts << "--author=#{opts[:author]}" if opts[:author].is_a? String
97
+ arr_opts << "#{opts[:between][0].to_s}..#{opts[:between][1].to_s}" if (opts[:between] && opts[:between].size == 2)
98
+ arr_opts << opts[:object] if opts[:object].is_a? String
99
+
100
+ #
101
+ arr_opts << '--'
102
+ arr_opts << opts[:path_limiter] if opts[:path_limiter].is_a? String
103
+ #
104
+
105
+ full_log = command_lines('log', arr_opts, true)
106
+ process_commit_data(full_log)
107
+ end
108
+
109
+ def revparse(string)
110
+ return string if string =~ /[A-Fa-f0-9]{40}/ # passing in a sha - just no-op it
111
+ rev = ['head', 'remotes', 'tags'].map do |d|
112
+ File.join(@git_dir, 'refs', d, string)
113
+ end.find do |path|
114
+ File.file?(path)
115
+ end
116
+ return File.read(rev).chomp if rev
117
+ command('rev-parse', string)
118
+ end
119
+
120
+ def namerev(string)
121
+ command('name-rev', string).split[1]
122
+ end
123
+
124
+ def object_type(sha)
125
+ command('cat-file', ['-t', sha])
126
+ end
127
+
128
+ def object_size(sha)
129
+ command('cat-file', ['-s', sha]).to_i
130
+ end
131
+
132
+ # returns useful array of raw commit object data
133
+ def commit_data(sha)
134
+ sha = sha.to_s
135
+ cdata = command_lines('cat-file', ['commit', sha])
136
+ process_commit_data(cdata, sha, 0)
137
+ end
138
+
139
+ def process_commit_data(data, sha = nil, indent = 4)
140
+ in_message = false
141
+
142
+ if sha
143
+ hsh = {'sha' => sha, 'message' => '', 'parent' => []}
144
+ else
145
+ hsh_array = []
146
+ end
147
+
148
+ data.each do |line|
149
+ line = line.chomp
150
+ if line == ''
151
+ in_message = !in_message
152
+ elsif in_message
153
+ hsh['message'] << line[indent..-1] << "\n"
154
+ else
155
+ data = line.split
156
+ key = data.shift
157
+ value = data.join(' ')
158
+ if key == 'commit'
159
+ sha = value
160
+ hsh_array << hsh if hsh
161
+ hsh = {'sha' => sha, 'message' => '', 'parent' => []}
162
+ end
163
+ if key == 'parent'
164
+ hsh[key] << value
165
+ else
166
+ hsh[key] = value
167
+ end
168
+ end
169
+ end
170
+
171
+ if hsh_array
172
+ hsh_array << hsh if hsh
173
+ hsh_array
174
+ else
175
+ hsh
176
+ end
177
+ end
178
+
179
+ def object_contents(sha, &block)
180
+ command('cat-file', ['-p', sha], &block)
181
+ end
182
+
183
+ def ls_tree(sha, options={})
184
+ # -- implement recursive ls-tree
185
+ command_args = []
186
+ command_args << '-r' if options[:recursive]
187
+ command_args << sha
188
+ #
189
+ data = {'blob' => {}, 'tree' => {}}
190
+
191
+ #
192
+ command_lines('ls-tree', command_args).each do |line|
193
+ #
194
+ (info, filenm) = line.split("\t")
195
+ (mode, type, sha) = info.split
196
+ data[type][filenm] = {:mode => mode, :sha => sha}
197
+ end
198
+
199
+ data
200
+ end
201
+
202
+ def mv(file1, file2)
203
+ command_lines('mv', ['--', file1, file2])
204
+ end
205
+
206
+ def full_tree(sha)
207
+ command_lines('ls-tree', ['-r', sha])
208
+ end
209
+
210
+ def tree_depth(sha)
211
+ full_tree(sha).size
212
+ end
213
+
214
+ def change_head_branch(branch_name)
215
+ command('symbolic-ref', ['HEAD', "refs/heads/#{branch_name}"])
216
+ end
217
+
218
+ def branches_all
219
+ arr = []
220
+ command_lines('branch', '-a').each do |b|
221
+ current = (b[0, 2] == '* ')
222
+ arr << [b.gsub('* ', '').strip, current]
223
+ end
224
+ arr
225
+ end
226
+
227
+ def list_files(ref_dir)
228
+ dir = File.join(@git_dir, 'refs', ref_dir)
229
+ files = []
230
+ Dir.chdir(dir) { files = Dir.glob('**/*').select { |f| File.file?(f) } } rescue nil
231
+ files
232
+ end
233
+
234
+ def branch_current
235
+ branches_all.select { |b| b[1] }.first[0] rescue nil
236
+ end
237
+
238
+
239
+ # returns hash
240
+ # [tree-ish] = [[line_no, match], [line_no, match2]]
241
+ # [tree-ish] = [[line_no, match], [line_no, match2]]
242
+ def grep(string, opts = {})
243
+ opts[:object] ||= 'HEAD'
244
+
245
+ grep_opts = ['-n']
246
+ grep_opts << '-i' if opts[:ignore_case]
247
+ grep_opts << '-v' if opts[:invert_match]
248
+ grep_opts << '-e'
249
+ grep_opts << string
250
+ grep_opts << opts[:object] if opts[:object].is_a?(String)
251
+ grep_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String
252
+
253
+ hsh = {}
254
+ command_lines('grep', grep_opts).each do |line|
255
+ if m = /(.*)\:(\d+)\:(.*)/.match(line)
256
+ hsh[m[1]] ||= []
257
+ hsh[m[1]] << [m[2].to_i, m[3]]
258
+ end
259
+ end
260
+ hsh
261
+ end
262
+
263
+ def diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})
264
+ diff_opts = ['-p']
265
+ diff_opts << obj1
266
+ diff_opts << obj2 if obj2.is_a?(String)
267
+ diff_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String
268
+
269
+ command('diff', diff_opts)
270
+ end
271
+
272
+ def diff_stats(obj1 = 'HEAD', obj2 = nil, opts = {})
273
+ diff_opts = ['--numstat']
274
+ diff_opts << obj1
275
+ diff_opts << obj2 if obj2.is_a?(String)
276
+ diff_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String
277
+
278
+ hsh = {:total => {:insertions => 0, :deletions => 0, :lines => 0, :files => 0}, :files => {}}
279
+
280
+ command_lines('diff', diff_opts).each do |file|
281
+ (insertions, deletions, filename) = file.split("\t")
282
+ hsh[:total][:insertions] += insertions.to_i
283
+ hsh[:total][:deletions] += deletions.to_i
284
+ hsh[:total][:lines] = (hsh[:total][:deletions] + hsh[:total][:insertions])
285
+ hsh[:total][:files] += 1
286
+ hsh[:files][filename] = {:insertions => insertions.to_i, :deletions => deletions.to_i}
287
+ end
288
+
289
+ hsh
290
+ end
291
+
292
+ # compares the index and the working directory
293
+ def diff_files
294
+ hsh = {}
295
+ command_lines('diff-files').each do |line|
296
+ (info, file) = line.split("\t")
297
+ (mode_src, mode_dest, sha_src, sha_dest, type) = info.split
298
+ hsh[file] = {:path => file, :mode_file => mode_src.to_s[1, 7], :mode_index => mode_dest,
299
+ :sha_file => sha_src, :sha_index => sha_dest, :type => type}
300
+ end
301
+ hsh
302
+ end
303
+
304
+ # compares the index and the repository
305
+ def diff_index(treeish)
306
+ hsh = {}
307
+ command_lines('diff-index', treeish).each do |line|
308
+ (info, file) = line.split("\t")
309
+ (mode_src, mode_dest, sha_src, sha_dest, type) = info.split
310
+ type = 'A' if type == 'M' && sha_dest !~ /^0+$/
311
+ hsh[file] = {:path => file, :mode_repo => mode_src.to_s[1, 7], :mode_index => mode_dest,
312
+ :sha_repo => sha_src, :sha_index => sha_dest, :type => type}
313
+ end
314
+ hsh
315
+ end
316
+
317
+ def ls_files(location=nil)
318
+ hsh = {}
319
+ command_lines('ls-files', ['--stage', location]).each do |line|
320
+ (info, file) = line.split("\t")
321
+ (mode, sha, stage) = info.split
322
+ file = eval(file) if file =~ /^\".*\"$/ # This takes care of quoted strings returned from git
323
+ hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage}
324
+ end
325
+ hsh
326
+ end
327
+
328
+
329
+ def ignored_files
330
+ command_lines('ls-files', ['--others', '-i', '--exclude-standard'])
331
+ end
332
+
333
+
334
+ def config_remote(name)
335
+ hsh = {}
336
+ config_list.each do |key, value|
337
+ if /remote.#{name}/.match(key)
338
+ hsh[key.gsub("remote.#{name}.", '')] = value
339
+ end
340
+ end
341
+ hsh
342
+ end
343
+
344
+ def config_get(name)
345
+ do_get = lambda do
346
+ command('config', ['--get', name])
347
+ end
348
+
349
+ if @git_dir
350
+ Dir.chdir(@git_dir, &do_get)
351
+ else
352
+ build_list.call
353
+ end
354
+ end
355
+
356
+ def global_config_get(name)
357
+ command('config', ['--global', '--get', name], false)
358
+ end
359
+
360
+ def config_list
361
+ build_list = lambda do |path|
362
+ parse_config_list command_lines('config', ['--list'])
363
+ end
364
+
365
+ if @git_dir
366
+ Dir.chdir(@git_dir, &build_list)
367
+ else
368
+ build_list.call
369
+ end
370
+ end
371
+
372
+ def global_config_list
373
+ parse_config_list command_lines('config', ['--global', '--list'], false)
374
+ end
375
+
376
+ def parse_config_list(lines)
377
+ hsh = {}
378
+ lines.each do |line|
379
+ (key, *values) = line.split('=')
380
+ hsh[key] = values.join('=')
381
+ end
382
+ hsh
383
+ end
384
+
385
+ def parse_config(file)
386
+ hsh = {}
387
+ parse_config_list command_lines('config', ['--list', '--file', file], false)
388
+ #hsh = {}
389
+ #file = File.expand_path(file)
390
+ #if File.file?(file)
391
+ # current_section = nil
392
+ # File.readlines(file).each do |line|
393
+ # if m = /\[(\w+)\]/.match(line)
394
+ # current_section = m[1]
395
+ # elsif m = /\[(\w+?) "(.*?)"\]/.match(line)
396
+ # current_section = "#{m[1]}.#{m[2]}"
397
+ # elsif m = /(\w+?) = (.*)/.match(line)
398
+ # key = "#{current_section}.#{m[1]}"
399
+ # hsh[key] = m[2]
400
+ # end
401
+ # end
402
+ #end
403
+ #hsh
404
+ end
405
+
406
+ ## WRITE COMMANDS ##
407
+
408
+ def config_set(name, value)
409
+ command('config', [name, value])
410
+ end
411
+
412
+ def global_config_set(name, value)
413
+ command('config', ['--global', name, value], false)
414
+ end
415
+
416
+ def add(path = '.')
417
+ arr_opts = ['--']
418
+ if path.is_a?(Array)
419
+ arr_opts += path
420
+ else
421
+ arr_opts << path
422
+ end
423
+ command('add', arr_opts)
424
+ end
425
+
426
+ def remove(path = '.', opts = {})
427
+ arr_opts = ['-f'] # overrides the up-to-date check by default
428
+ arr_opts << ['-r'] if opts[:recursive]
429
+ arr_opts << '--'
430
+ if path.is_a?(Array)
431
+ arr_opts += path
432
+ else
433
+ arr_opts << path
434
+ end
435
+
436
+ command('rm', arr_opts)
437
+ end
438
+
439
+ #
440
+ def commit(message, opts = {})
441
+ arr_opts = []
442
+ arr_opts << '-m' << message unless !!opts[:reuse_message]
443
+ arr_opts << '-C' << opts[:reuse_message] if !!opts[:reuse_message]
444
+ arr_opts << '-a' if opts[:add_all]
445
+ arr_opts << '--allow-empty' if opts[:allow_empty]
446
+ arr_opts << "--author" << opts[:author] if opts[:author]
447
+ command('commit', arr_opts)
448
+ end
449
+ #
450
+
451
+ def reset(commit, opts = {})
452
+ arr_opts = []
453
+ arr_opts << '--hard' if opts[:hard]
454
+ arr_opts << commit if commit
455
+ command('reset', arr_opts)
456
+ end
457
+
458
+ def apply(patch_file)
459
+ arr_opts = []
460
+ arr_opts << '--' << patch_file if patch_file
461
+ command('apply', arr_opts)
462
+ end
463
+
464
+ def apply_mail(patch_file)
465
+ arr_opts = []
466
+ arr_opts << '--' << patch_file if patch_file
467
+ command('am', arr_opts)
468
+ end
469
+
470
+ def stashes_all
471
+ arr = []
472
+ filename = File.join(@git_dir, 'logs/refs/stash')
473
+ if File.exist?(filename)
474
+ File.open(filename).each_with_index { |line, i|
475
+ m = line.match(/:(.*)$/)
476
+ arr << [i, m[1].strip]
477
+ }
478
+ end
479
+ arr
480
+ end
481
+
482
+ def stash_save(message)
483
+ output = command('stash save', ['--', message])
484
+ output =~ /HEAD is now at/
485
+ end
486
+
487
+ def stash_apply(id = nil)
488
+ if id
489
+ command('stash apply', [id])
490
+ else
491
+ command('stash apply')
492
+ end
493
+ end
494
+
495
+ def stash_clear
496
+ command('stash clear')
497
+ end
498
+
499
+ def stash_list
500
+ command('stash list')
501
+ end
502
+
503
+ # -- implement --t switch on git branch command
504
+ def branch_new(branch, track=nil)
505
+ options = [branch]
506
+ options << '--t' << track if track
507
+ command('branch', options)
508
+ end
509
+ #
510
+
511
+ def branch_delete(branch)
512
+ command('branch', ['-D', branch])
513
+ end
514
+
515
+ def checkout(branch, opts = {})
516
+ arr_opts = []
517
+ arr_opts << '-f' if opts[:force]
518
+
519
+ # -- implementation of a working -b switch
520
+ # for git checkout command
521
+ arr_opts << '-b' if opts[:new_branch]
522
+ # -- this push is legacy
523
+ # i don't understand it and congratulate
524
+ # myself not to try to and just leave it here
525
+ arr_opts << opts[:new_branch] if opts[:new_branch] && !opts[:new_branch].is_a?(TrueClass)
526
+ #
527
+
528
+ arr_opts << branch if branch
529
+
530
+ # -- we can 'git checkout `sha`' now
531
+ arr_opts << opts[:commit] if opts[:commit]
532
+ #
533
+
534
+ # -- checkout paths
535
+ arr_opts.push '--', *opts[:files] if opts[:files]
536
+ #
537
+
538
+ command('checkout', arr_opts)
539
+ end
540
+
541
+ def checkout_file(version, file)
542
+ arr_opts = []
543
+ arr_opts << version
544
+ arr_opts << file
545
+ command('checkout', arr_opts)
546
+ end
547
+
548
+ def merge(branch, message = nil)
549
+ arr_opts = []
550
+ arr_opts << '-m' << message if message
551
+ arr_opts += [branch]
552
+ command('merge', arr_opts)
553
+ end
554
+
555
+ def unmerged
556
+ unmerged = []
557
+ command_lines('diff', ["--cached"]).each do |line|
558
+ unmerged << $1 if line =~ /^\* Unmerged path (.*)/
559
+ end
560
+ unmerged
561
+ end
562
+
563
+ def conflicts # :yields: file, your, their
564
+ self.unmerged.each do |f|
565
+ your = Tempfile.new("YOUR-#{File.basename(f)}").path
566
+ command('show', ":2:#{f}", true, "> #{escape your}")
567
+
568
+ their = Tempfile.new("THEIR-#{File.basename(f)}").path
569
+ command('show', ":3:#{f}", true, "> #{escape their}")
570
+ yield(f, your, their)
571
+ end
572
+ end
573
+
574
+ def remote_add(name, url, opts = {})
575
+ arr_opts = ['add']
576
+ arr_opts << '-f' if opts[:with_fetch]
577
+ arr_opts << '--'
578
+ arr_opts << name
579
+ arr_opts << url
580
+
581
+ command('remote', arr_opts)
582
+ end
583
+
584
+ # this is documented as such, but seems broken for some reason
585
+ # i'll try to get around it some other way later
586
+ def remote_remove(name)
587
+ command('remote', ['rm', '--', name])
588
+ end
589
+
590
+ def remotes
591
+ command_lines('remote')
592
+ end
593
+
594
+ def tags
595
+ command_lines('tag')
596
+ end
597
+
598
+ def tag(tag)
599
+ command('tag', tag)
600
+ end
601
+
602
+
603
+ def fetch(remote)
604
+ command('fetch', remote)
605
+ end
606
+
607
+ def push(remote, branch = 'master', tags = false)
608
+ command('push', [remote, branch])
609
+ command('push', ['--tags', remote]) if tags
610
+ end
611
+
612
+ def tag_sha(tag_name)
613
+ head = File.join(@git_dir, 'refs', 'tags', tag_name)
614
+ return File.read(head).chomp if File.exists?(head)
615
+
616
+ command('show-ref', ['--tags', '-s', tag_name])
617
+ end
618
+
619
+ def repack
620
+ command('repack', ['-a', '-d'])
621
+ end
622
+
623
+ def gc
624
+ command('gc', ['--prune', '--aggressive', '--auto'])
625
+ end
626
+
627
+ # reads a tree into the current index file
628
+ def read_tree(treeish, opts = {})
629
+ arr_opts = []
630
+ arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
631
+ arr_opts += [treeish]
632
+ command('read-tree', arr_opts)
633
+ end
634
+
635
+ def write_tree
636
+ command('write-tree')
637
+ end
638
+
639
+ def commit_tree(tree, opts = {})
640
+ opts[:message] ||= "commit tree #{tree}"
641
+ t = Tempfile.new('commit-message')
642
+ t.write(opts[:message])
643
+ t.close
644
+
645
+ arr_opts = []
646
+ arr_opts << tree
647
+ arr_opts << '-p' << opts[:parent] if opts[:parent]
648
+ arr_opts += [opts[:parents]].map { |p| ['-p', p] }.flatten if opts[:parents]
649
+ command('commit-tree', arr_opts, true, "< #{escape t.path}")
650
+ end
651
+
652
+ def update_ref(branch, commit)
653
+ command('update-ref', [branch, commit])
654
+ end
655
+
656
+ def checkout_index(opts = {})
657
+ arr_opts = []
658
+ arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
659
+ arr_opts << "--force" if opts[:force]
660
+ arr_opts << "--all" if opts[:all]
661
+ arr_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String
662
+
663
+ command('checkout-index', arr_opts)
664
+ end
665
+
666
+ # creates an archive file
667
+ #
668
+ # options
669
+ # :format (zip, tar)
670
+ # :prefix
671
+ # :remote
672
+ # :path
673
+ def archive(sha, file = nil, opts = {})
674
+ opts[:format] ||= 'zip'
675
+
676
+ if opts[:format] == 'tgz'
677
+ opts[:format] = 'tar'
678
+ opts[:add_gzip] = true
679
+ end
680
+
681
+ file ||= Tempfile.new('archive').path
682
+
683
+ arr_opts = []
684
+ arr_opts << "--format=#{opts[:format]}" if opts[:format]
685
+ arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
686
+ arr_opts << "--remote=#{opts[:remote]}" if opts[:remote]
687
+ arr_opts << sha
688
+ arr_opts << '--' << opts[:path] if opts[:path]
689
+ command('archive', arr_opts, true, (opts[:add_gzip] ? '| gzip' : '') + " > #{escape file}")
690
+ return file
691
+ end
692
+
693
+ # returns the current version of git, as an Array of Fixnums.
694
+ def current_command_version
695
+ output = command('version', [], false)
696
+ version = output[/\d+\.\d+(\.\d+)+/]
697
+ version.split('.').collect {|i| i.to_i}
698
+ end
699
+
700
+ def required_command_version
701
+ [1, 6, 0, 0]
702
+ end
703
+
704
+ def meets_required_version?
705
+ current_version = self.current_command_version
706
+ required_version = self.required_command_version
707
+
708
+ return current_version[0] >= required_version[0] &&
709
+ current_version[1] >= required_version[1] &&
710
+ (current_version[2] ? current_version[2] >= required_version[2] : true) &&
711
+ (current_version[3] ? current_version[3] >= required_version[3] : true)
712
+ end
713
+
714
+
715
+ private
716
+
717
+ def command_lines(cmd, opts = [], chdir = true, redirect = '')
718
+ command(cmd, opts, chdir).split("\n")
719
+ end
720
+
721
+ def command(cmd, opts = [], chdir = true, redirect = '', &block)
722
+ ENV['GIT_DIR'] = @git_dir
723
+ ENV['GIT_INDEX_FILE'] = @git_index_file
724
+ ENV['GIT_WORK_TREE'] = @git_work_dir
725
+ path = @git_work_dir || @git_dir || @path
726
+
727
+ opts = [opts].flatten.map {|s| escape(s) }.join(' ')
728
+ git_cmd = "git #{cmd} #{opts} #{redirect} 2>&1"
729
+
730
+ out = nil
731
+ if chdir && (Dir.getwd != path)
732
+ Dir.chdir(path) { out = run_command(git_cmd, &block) }
733
+ else
734
+ out = run_command(git_cmd, &block)
735
+ end
736
+
737
+ if @logger
738
+ @logger.info(git_cmd)
739
+ @logger.debug(out)
740
+ end
741
+
742
+ if $?.exitstatus > 0
743
+ if $?.exitstatus == 1 && out == ''
744
+ return ''
745
+ end
746
+ raise Git::GitExecuteError.new(git_cmd + ':' + out.to_s)
747
+ end
748
+ out
749
+ end
750
+
751
+ def run_command(git_cmd, &block)
752
+ if block_given?
753
+ IO.popen(git_cmd, &block)
754
+ else
755
+ `#{git_cmd}`.chomp
756
+ end
757
+ end
758
+
759
+ def escape(s)
760
+ escaped = s.to_s.gsub('\'', '\'\\\'\'')
761
+ %Q{"#{escaped}"}
762
+ end
763
+
764
+ end
765
+ end