gitreport 0.0.2 → 0.0.3

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