| Class | Grit::GitRuby::Repository |
| In: |
lib/grit/git-ruby/repository.rb
|
| Parent: | Object |
| git_dir | [RW] | |
| options | [RW] |
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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 a raw object given a SHA1
# 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
# 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
# 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 packfiles
# 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
# 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
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
# File lib/grit/git-ruby/repository.rb, line 258
258: def log(sha, options = {})
259: @already_searched = {}
260: walk_log(sha, options)
261: end
# 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 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
# 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
# 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
# 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
# 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
# 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] ]
# 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
# 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
# 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
# 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
# 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