Class Grit::GitRuby::Repository
In: lib/grit/git-ruby/repository.rb
Parent: Object

Methods

Classes and Modules

Class Grit::GitRuby::Repository::NoSuchPath
Class Grit::GitRuby::Repository::NoSuchShaFound

Attributes

git_dir  [RW] 
options  [RW] 

Public Class methods

[Source]

     # File lib/grit/git-ruby/repository.rb, line 645
645:       def self.add_file(name, contents)
646:         File.open(name, 'w') do |f|
647:           f.write contents
648:         end
649:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 639
639:       def self.create_initial_config(bare = false)
640:         bare ? bare_status = 'true' : bare_status = 'false'
641:         config = "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = #{bare_status}\n\tlogallrefupdates = true"
642:         add_file('config', config)
643:       end

initialize a git repository

[Source]

     # File lib/grit/git-ruby/repository.rb, line 605
605:       def self.init(dir, bare = false)
606: 
607:         FileUtils.mkdir_p(dir) if !File.exists?(dir)
608: 
609:         FileUtils.cd(dir) do
610:           if(File.exists?('objects'))
611:             return false # already initialized
612:           else
613:             # initialize directory
614:             create_initial_config(bare)
615:             FileUtils.mkdir_p('refs/heads')
616:             FileUtils.mkdir_p('refs/tags')
617:             FileUtils.mkdir_p('objects/info')
618:             FileUtils.mkdir_p('objects/pack')
619:             FileUtils.mkdir_p('branches')
620:             add_file('description', 'Unnamed repository; edit this file to name it for gitweb.')
621:             add_file('HEAD', "ref: refs/heads/master\n")
622:             FileUtils.mkdir_p('hooks')
623:             FileUtils.cd('hooks') do
624:               add_file('applypatch-msg', '# add shell script and make executable to enable')
625:               add_file('post-commit', '# add shell script and make executable to enable')
626:               add_file('post-receive', '# add shell script and make executable to enable')
627:               add_file('post-update', '# add shell script and make executable to enable')
628:               add_file('pre-applypatch', '# add shell script and make executable to enable')
629:               add_file('pre-commit', '# add shell script and make executable to enable')
630:               add_file('pre-rebase', '# add shell script and make executable to enable')
631:               add_file('update', '# add shell script and make executable to enable')
632:             end
633:             FileUtils.mkdir_p('info')
634:             add_file('info/exclude', "# *.[oa]\n# *~")
635:           end
636:         end
637:       end

[Source]

    # File lib/grit/git-ruby/repository.rb, line 37
37:       def initialize(git_dir, options = {})
38:         @git_dir = git_dir
39:         @options = options
40:         @packs = []
41:       end

Public Instance methods

[Source]

     # File lib/grit/git-ruby/repository.rb, line 536
536:       def blame_tree(commit_sha, path)
537:         # find subtree
538:         tree_sha = get_subtree(commit_sha, path)
539:         return {} if !tree_sha
540: 
541:         looking_for = []
542:         get_object_by_sha1(tree_sha).entry.each do |e|
543:           looking_for << File.join('.', e.name)
544:         end
545:                         
546:         @already_searched = {}
547:         commits = look_for_commits(commit_sha, path, looking_for)
548:         
549:         # cleaning up array
550:         arr = {}
551:         commits.each do |commit_array|
552:           key = commit_array[0].gsub('./', '')
553:           arr[key] = commit_array[1]
554:         end
555:         arr
556:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 99
 99:       def cached(key, object, do_cache = true)
100:         object
101:       end

returns the raw file contents of this sha

[Source]

     # File lib/grit/git-ruby/repository.rb, line 154
154:       def cat_file(sha)
155:         get_object_by_sha1(sha).raw_content
156:       end

returns the file size (as an int) of this sha

[Source]

     # File lib/grit/git-ruby/repository.rb, line 149
149:       def cat_file_size(sha)
150:         get_raw_object_by_sha1(sha).content.size
151:       end

returns the file type (as a symbol) of this sha

[Source]

     # File lib/grit/git-ruby/repository.rb, line 144
144:       def cat_file_type(sha)
145:         get_raw_object_by_sha1(sha).type
146:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 651
651:       def close
652:         @packs.each do |pack|
653:           pack.close
654:         end if @packs
655:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 368
368:       def diff(commit1, commit2, options = {})
369:         patch = ''
370:         
371:         commit_obj1 = get_object_by_sha1(commit1)
372:         tree1 = commit_obj1.tree
373:         if commit2
374:           tree2 = get_object_by_sha1(commit2).tree
375:         else
376:           tree2 = get_object_by_sha1(commit_obj1.parent.first).tree
377:         end
378:         
379:         qdiff = quick_diff(tree1, tree2)
380:         
381:         qdiff.sort.each do |diff_arr|
382:           format, lines, output = :unified, 3, ''
383:           file_length_difference = 0
384:           
385:           fileA = (diff_arr[2]) ? cat_file(diff_arr[2]) : ''
386:           fileB = (diff_arr[3]) ? cat_file(diff_arr[3]) : ''
387:           
388:           sha1 = (diff_arr[2]) ? diff_arr[2] : '0000000000000000000000000000000000000000'
389:           sha2 = (diff_arr[3]) ? diff_arr[3] : '0000000000000000000000000000000000000000'
390: 
391:           data_old = fileA.split(/\n/).map! { |e| e.chomp }
392:           data_new = fileB.split(/\n/).map! { |e| e.chomp }
393:           
394:           diffs = Difference::LCS.diff(data_old, data_new)    
395:           next if diffs.empty?
396: 
397:           header = 'diff --git a/' + diff_arr[0].gsub('./', '') + ' b/' + diff_arr[0].gsub('./', '')
398:           if options[:full_index]
399:             header << "\n" + 'index ' + sha1 + '..' + sha2
400:             header << ' 100644' if diff_arr[3] # hard coding this because i don't think we use it
401:           else
402:             header << "\n" + 'index ' + sha1[0,7] + '..' + sha2[0,7]
403:             header << ' 100644' if diff_arr[3] # hard coding this because i don't think we use it
404:           end
405:           header << "\n--- " + 'a/' + diff_arr[0].gsub('./', '')
406:           header << "\n+++ " + 'b/' + diff_arr[0].gsub('./', '')
407:           header += "\n"
408:           
409:           oldhunk = hunk = nil
410: 
411:           diffs.each do |piece|
412:             begin
413:               hunk = Difference::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
414:               file_length_difference = hunk.file_length_difference
415: 
416:               next unless oldhunk
417: 
418:               if lines > 0 && hunk.overlaps?(oldhunk)
419:                 hunk.unshift(oldhunk)
420:               else
421:                 output << oldhunk.diff(format)
422:               end
423:             ensure
424:               oldhunk = hunk
425:               output << "\n"
426:             end
427:           end
428:           
429:           output << oldhunk.diff(format)
430:           output << "\n"
431:           
432:           patch << header + output.lstrip      
433:         end
434:         patch
435:       rescue
436:         '' # one of the trees was bad or lcs isn't there - no diff 
437:       end

returns true if the files in path_limiter were changed, or no path limiter used by the log() function when passed with a path_limiter

[Source]

     # File lib/grit/git-ruby/repository.rb, line 504
504:       def files_changed?(tree_sha1, tree_sha2, path_limiter = nil)
505:         if path_limiter
506:           mod = quick_diff(tree_sha1, tree_sha2)
507:           files = mod.map { |c| c.first }
508:           path_limiter.to_a.each do |filepath|
509:             if files.include?(filepath)
510:               return true
511:             end
512:           end
513:           return false
514:         end
515:         true
516:       end

returns GitRuby object of any type given a SHA1

[Source]

     # File lib/grit/git-ruby/repository.rb, line 104
104:       def get_object_by_sha1(sha1)
105:         r = get_raw_object_by_sha1(sha1)
106:         return nil if !r
107:         GitObject.from_raw(r)
108:       end

returns a raw object given a SHA1

[Source]

    # File lib/grit/git-ruby/repository.rb, line 72
72:       def get_raw_object_by_sha1(sha1o)
73:         raise NoSuchShaFound if sha1o.nil? || sha1o.empty? || !sha1o.is_a?(String)
74: 
75:         sha1 = [sha1o.chomp].pack("H*")
76:         # try packs
77:         packs.each do |pack|
78:           o = pack[sha1]
79:           return pack[sha1] if o
80:         end
81: 
82:         # try loose storage
83:         loose.each do |lsobj|
84:           o = lsobj[sha1]
85:           return o if o
86:         end
87: 
88:         # try packs again, maybe the object got packed in the meantime
89:         initpacks
90:         packs.each do |pack|
91:           o = pack[sha1]
92:           return o if o
93:         end
94: 
95: #        puts "*#{sha1o}*"
96:         raise NoSuchShaFound
97:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 185
185:       def get_raw_tree(sha)
186:         o = get_raw_object_by_sha1(sha)
187:         if o.type == :commit
188:           cat_file(get_object_by_sha1(sha).tree)
189:         elsif o.type == :tag
190:           commit_sha = get_object_by_sha1(sha).object
191:           cat_file(get_object_by_sha1(commit_sha).tree)
192:         elsif o.type == :tree
193:           cat_file(sha)
194:         end
195:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 518
518:       def get_subtree(commit_sha, path)
519:         tree_sha = get_object_by_sha1(commit_sha).tree
520:         
521:         if path && !(path == '' || path == '.' || path == './')
522:           paths = path.split('/')
523:           paths.each do |path|
524:             tree = get_object_by_sha1(tree_sha)
525:             if entry = tree.entry.select { |e| e.name == path }.first
526:               tree_sha = entry.sha1 rescue nil
527:             else
528:               return false
529:             end
530:           end
531:         end
532:         
533:         tree_sha
534:       end

returns true if the hex-packed sha is in the loose objects

[Source]

     # File lib/grit/git-ruby/repository.rb, line 135
135:       def in_loose?(sha_hex)
136:         loose.each do |lsobj|
137:           return true if lsobj[sha_hex]
138:         end
139:         false
140:       end

returns true if the hex-packed sha is in the packfiles

[Source]

     # File lib/grit/git-ruby/repository.rb, line 126
126:       def in_packs?(sha_hex)
127:         # try packs
128:         packs.each do |pack|
129:           return true if pack[sha_hex]
130:         end
131:         false
132:       end

returns a 2-d hash of the tree

‘blob’][‘FILENAME‘
= {:mode => ‘100644’, :sha => SHA}
‘tree’][‘DIRNAME‘
= {:mode => ‘040000’, :sha => SHA}

[Source]

     # File lib/grit/git-ruby/repository.rb, line 161
161:       def list_tree(sha)        
162:         data = {'blob' => {}, 'tree' => {}, 'link' => {}, 'commit' => {}}
163:         get_object_by_sha1(sha).entry.each do |e|
164:           data[e.format_type][e.name] = {:mode => e.format_mode, :sha => e.sha1}
165:         end 
166:         data
167:       end

returns an array of GitRuby Commit objects

[sha, raw_output], [sha, raw_output], [sha, raw_output
… ]

takes the following options:

 :since - Time object specifying that you don't want commits BEFORE this
 :until - Time object specifying that you don't want commit AFTER this
 :first_parent - tells log to only walk first parent
 :path_limiter - string or array of strings to limit path
 :max_count - number to limit the output

[Source]

     # File lib/grit/git-ruby/repository.rb, line 258
258:       def log(sha, options = {})
259:         @already_searched = {}
260:         walk_log(sha, options)
261:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 558
558:       def look_for_commits(commit_sha, path, looking_for, options = {})        
559:         return [] if @already_searched[commit_sha] # to prevent rechecking branches
560:                 
561:         @already_searched[commit_sha] = true
562:         
563:         commit = get_object_by_sha1(commit_sha)
564:         tree_sha = get_subtree(commit_sha, path)
565: 
566:         found_data = []
567:         
568:         # at the beginning of the branch
569:         if commit.parent.size == 0  
570:           looking_for.each do |search|
571:             # prevents the rare case of multiple branch starting points with 
572:             # files that have never changed
573:             if found_data.assoc(search) 
574:               found_data << [search, commit_sha]
575:             end
576:           end
577:           return found_data
578:         end
579:         
580:         # go through the parents recursively, looking for somewhere this has been changed
581:         commit.parent.each do |pc|
582:           diff = quick_diff(tree_sha, get_subtree(pc, path), '.', false)
583:           
584:           # remove anything found
585:           looking_for.each do |search|
586:             if match = diff.assoc(search)
587:               found_data << [search, commit_sha, match]
588:               looking_for.delete(search)
589:             end
590:           end
591:           
592:           if looking_for.size <= 0  # we're done
593:             return found_data
594:           end
595: 
596:           found_data += look_for_commits(pc, path, looking_for)  # recurse into parent
597:           return found_data if options[:first_parent]
598:         end
599:         
600:         ## TODO : find most recent commit with change in any parent
601:         found_data
602:       end

returns the loose objects object lazily

[Source]

    # File lib/grit/git-ruby/repository.rb, line 44
44:       def loose
45:         @loose ||= initloose
46:       end

returns the raw (cat-file) output for a tree if given a commit sha, it will print the tree of that commit if given a path limiter array, it will limit the output to those

[Source]

     # File lib/grit/git-ruby/repository.rb, line 172
172:       def ls_tree(sha, paths = [])
173:         if paths.size > 0
174:           # pathing
175:           part = []
176:           paths.each do |path|
177:             part += ls_tree_path(sha, path)
178:           end
179:           return part.join("\n")
180:         else
181:           get_raw_tree(sha)
182:         end
183:       end

return array of tree entries

 TODO : refactor this to remove the fugly

[Source]

     # File lib/grit/git-ruby/repository.rb, line 199
199:       def ls_tree_path(sha, path, append = nil)
200:         tree = get_raw_tree(sha)
201:         if path =~ /\//
202:           paths = path.split('/')
203:           last = path[path.size - 1, 1]
204:           if (last == '/') && (paths.size == 1)
205:             append = append ? File.join(append, paths.first) : paths.first
206:             dir_name = tree.split("\n").select { |p| p.split("\t")[1] == paths.first }.first
207:             raise NoSuchPath if !dir_name
208:             next_sha = dir_name.split(' ')[2]
209:             tree = get_raw_tree(next_sha)
210:             tree = tree.split("\n")
211:             if append
212:               mod_tree = []
213:               tree.each do |ent|
214:                 (info, fpath) = ent.split("\t")
215:                 mod_tree << [info, File.join(append, fpath)].join("\t")
216:               end
217:               mod_tree
218:             else
219:               tree
220:             end
221:           else
222:             next_path = paths.shift
223:             dir_name = tree.split("\n").select { |p| p.split("\t")[1] == next_path }.first
224:             raise NoSuchPath if !dir_name
225:             next_sha = dir_name.split(' ')[2]
226:             next_path = append ? File.join(append, next_path) : next_path
227:             if (last == '/')
228:               ls_tree_path(next_sha, paths.join("/") + '/', next_path)
229:             else
230:               ls_tree_path(next_sha, paths.join("/"), next_path)
231:             end
232:           end
233:         else
234:           tree = tree.split("\n")
235:           tree = tree.select { |p| p.split("\t")[1] == path }
236:           if append
237:             mod_tree = []
238:             tree.each do |ent|
239:               (info, fpath) = ent.split("\t")
240:               mod_tree << [info, File.join(append, fpath)].join("\t")
241:             end
242:             mod_tree
243:           else
244:             tree
245:           end
246:         end
247:       end

returns true or false if that sha exists in the db

[Source]

     # File lib/grit/git-ruby/repository.rb, line 116
116:       def object_exists?(sha1)
117:         sha_hex = [sha1].pack("H*")
118:         return true if in_packs?(sha_hex)
119:         return true if in_loose?(sha_hex)
120:         initpacks
121:         return true if in_packs?(sha_hex) #maybe the object got packed in the meantime
122:         false
123:       end

returns the array of pack list objects

[Source]

    # File lib/grit/git-ruby/repository.rb, line 49
49:       def packs
50:         @packs ||= initpacks
51:       end

writes a raw object into the git repo

[Source]

     # File lib/grit/git-ruby/repository.rb, line 111
111:       def put_raw_object(content, type)
112:         loose.first.put_raw_object(content, type)
113:       end

takes 2 tree shas and recursively walks them to find out what files or directories have been modified in them and returns an array of changes [ [full_path, ‘added’, tree1_hash, nil],

  [full_path, 'removed', nil, tree2_hash],
  [full_path, 'modified', tree1_hash, tree2_hash]
 ]

[Source]

     # File lib/grit/git-ruby/repository.rb, line 446
446:       def quick_diff(tree1, tree2, path = '.', recurse = true)
447:          # handle empty trees
448:          changed = []
449:          return changed if tree1 == tree2
450:          
451:          t1 = list_tree(tree1) if tree1
452:          t2 = list_tree(tree2) if tree2
453:         
454:          # finding files that are different
455:          t1['blob'].each do |file, hsh|
456:            t2_file = t2['blob'][file] rescue nil
457:            full = File.join(path, file)
458:            if !t2_file
459:              changed << [full, 'added', hsh[:sha], nil]      # not in parent
460:            elsif (hsh[:sha] != t2_file[:sha])
461:              changed << [full, 'modified', hsh[:sha], t2_file[:sha]]   # file changed
462:            end
463:          end if t1
464:          t2['blob'].each do |file, hsh|
465:            if !t1 || !t1['blob'][file]
466:              changed << [File.join(path, file), 'removed', nil, hsh[:sha]]
467:            end
468:          end if t2
469: 
470:          t1['tree'].each do |dir, hsh|
471:            t2_tree = t2['tree'][dir] rescue nil
472:            full = File.join(path, dir)
473:            if !t2_tree
474:              if recurse
475:                changed += quick_diff(hsh[:sha], nil, full, true) 
476:              else
477:                changed << [full, 'added', hsh[:sha], nil]      # not in parent
478:              end
479:            elsif (hsh[:sha] != t2_tree[:sha])
480:              if recurse
481:                changed += quick_diff(hsh[:sha], t2_tree[:sha], full, true)
482:              else
483:                changed << [full, 'modified', hsh[:sha], t2_tree[:sha]]   # file changed
484:              end
485:            end
486:          end if t1
487:          t2['tree'].each do |dir, hsh|
488:            t1_tree = t1['tree'][dir] rescue nil
489:            full = File.join(path, dir)
490:            if !t1_tree
491:              if recurse
492:                changed += quick_diff(nil, hsh[:sha], full, true) 
493:              else
494:                changed << [full, 'removed', nil, hsh[:sha]]
495:              end
496:            end
497:          end if t2
498: 
499:          changed
500:        end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 274
274:       def rev_list(sha, options)
275:         if sha.is_a? Array
276:           (end_sha, sha) = sha
277:         end
278:         
279:         log = log(sha, options)
280:         log = log.sort { |a, b| a[2] <=> b[2] }.reverse
281:                 
282:         if end_sha
283:           log = truncate_arr(log, end_sha)
284:         end
285:         
286:         # shorten the list if it's longer than max_count (had to get everything in branches)
287:         if options[:max_count]
288:           if (opt_len = options[:max_count].to_i) < log.size
289:             log = log[0, opt_len]
290:           end
291:         end
292:         
293:         if options[:pretty] == 'raw'
294:           log.map {|k, v| v }.join('')
295:         else
296:           log.map {|k, v| k }.join("\n")
297:         end
298:       end

prints out the type, shas and content of all of the pack files

[Source]

    # File lib/grit/git-ruby/repository.rb, line 55
55:       def show
56:         packs.each do |p|
57:           puts p.name
58:           puts
59:           p.each_sha1 do |s|
60:             puts "**#{p[s].type}**"
61:             if p[s].type.to_s == 'commit'
62:               puts s.unpack('H*')
63:               puts p[s].content
64:             end
65:           end
66:           puts
67:         end
68:       end

[Source]

     # File lib/grit/git-ruby/repository.rb, line 263
263:       def truncate_arr(arr, sha)
264:         new_arr = []
265:         arr.each do |a|
266:           if a[0] == sha
267:             return new_arr
268:           end
269:           new_arr << a
270:         end
271:         return new_arr
272:       end

called by log() to recursively walk the tree

[Source]

     # File lib/grit/git-ruby/repository.rb, line 301
301:       def walk_log(sha, opts, total_size = 0)
302:         return [] if @already_searched[sha] # to prevent rechecking branches
303:         @already_searched[sha] = true
304:         
305:         array = []          
306:         if (sha)          
307:           o = get_raw_object_by_sha1(sha)
308:           if o.type == :tag
309:             commit_sha = get_object_by_sha1(sha).object
310:             c = get_object_by_sha1(commit_sha)
311:           else
312:             c = GitObject.from_raw(o)
313:           end
314: 
315:           return [] if c.type != :commit
316:           
317:           add_sha = true
318:           
319:           if opts[:since] && opts[:since].is_a?(Time) && (opts[:since] > c.committer.date)
320:             add_sha = false
321:           end
322:           if opts[:until] && opts[:until].is_a?(Time) && (opts[:until] < c.committer.date)
323:             add_sha = false
324:           end
325:           
326:           # follow all parents unless '--first-parent' is specified #
327:           subarray = []
328:           
329:           if !c.parent.first && opts[:path_limiter]  # check for the last commit
330:             add_sha = false
331:           end
332:           
333:           if (!opts[:max_count] || ((array.size + total_size) < opts[:max_count]))
334:             
335:             if !opts[:path_limiter]
336:               output = c.raw_log(sha)
337:               array << [sha, output, c.committer.date]
338:             end
339:             
340:             if (opts[:max_count] && (array.size + total_size) >= opts[:max_count])
341:               return array
342:             end
343:             
344:             c.parent.each do |psha|
345:               if psha && !files_changed?(c.tree, get_object_by_sha1(psha).tree,
346:                                         opts[:path_limiter])
347:                 add_sha = false 
348:               end
349:               subarray += walk_log(psha, opts, (array.size + total_size)) 
350:               next if opts[:first_parent]
351:             end
352:           
353:             if opts[:path_limiter] && add_sha
354:               output = c.raw_log(sha)
355:               array << [sha, output, c.committer.date]
356:             end          
357:             
358:             if add_sha
359:               array += subarray
360:             end
361:           end
362:                                 
363:         end
364:         
365:         array
366:       end

Protected Instance methods

[Source]

     # File lib/grit/git-ruby/repository.rb, line 659
659:         def git_path(path)
660:           return "#@git_dir/#{path}"
661:         end

[Validate]