blackwinter-git 1.2.5

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