Skip to content

Commit

Permalink
added some blame and merge stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
schacon committed Dec 11, 2008
1 parent 1c36188 commit 1c002dd
Show file tree
Hide file tree
Showing 17 changed files with 345 additions and 26 deletions.
6 changes: 0 additions & 6 deletions .gitignore

This file was deleted.

2 changes: 1 addition & 1 deletion README.txt
Expand Up @@ -4,7 +4,7 @@ grit


== DESCRIPTION: == DESCRIPTION:


Grit is a Ruby library for extracting information from a git repository in and Grit is a Ruby library for extracting information from a git repository in an
object oriented manner. object oriented manner.


== REQUIREMENTS: == REQUIREMENTS:
Expand Down
2 changes: 2 additions & 0 deletions lib/grit.rb
Expand Up @@ -36,6 +36,8 @@
require 'grit/index' require 'grit/index'
require 'grit/status' require 'grit/status'
require 'grit/submodule' require 'grit/submodule'
require 'grit/blame'
require 'grit/merge'




module Grit module Grit
Expand Down
61 changes: 61 additions & 0 deletions lib/grit/blame.rb
@@ -0,0 +1,61 @@
module Grit

class Blame

attr_reader :lines

def initialize(repo, file, commit)
@repo = repo
@file = file
@commit = commit
@lines = []
load_blame
end

def load_blame
output = @repo.git.blame({'p' => true}, @commit, '--', @file)
process_raw_blame(output)
end

def process_raw_blame(output)
lines, final = [], []
info, commits = {}, {}

# process the output
output.split("\n").each do |line|
if line[0, 1] == "\t"
lines << line[1, line.size]
elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
if !commits[m[1]]
commits[m[1]] = @repo.commit(m[1])
end
info[m[3].to_i] = [commits[m[1]], m[2].to_i]
end
end

# get it together
info.sort.each do |lineno, commit|
final << BlameLine.new(lineno, commit[1], commit[0], lines[lineno - 1])
end

@lines = final
end

# Pretty object inspection
def inspect
%Q{#<Grit::Blame "#{@file} <#{@commit}>">}
end

class BlameLine
attr_accessor :lineno, :oldlineno, :commit, :line
def initialize(lineno, oldlineno, commit, line)
@lineno = lineno
@oldlineno = oldlineno
@commit = commit
@line = line
end
end

end # Blame

end # Grit
7 changes: 6 additions & 1 deletion lib/grit/commit.rb
Expand Up @@ -10,6 +10,7 @@ class Commit
lazy_reader :committed_date lazy_reader :committed_date
lazy_reader :message lazy_reader :message
lazy_reader :short_message lazy_reader :short_message
lazy_reader :author_string


# Instantiate a new Commit # Instantiate a new Commit
# +id+ is the id of the commit # +id+ is the id of the commit
Expand Down Expand Up @@ -205,7 +206,11 @@ def self.actor(line)
m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/) m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/)
[Actor.from_string(actor), Time.at(epoch.to_i)] [Actor.from_string(actor), Time.at(epoch.to_i)]
end end


def author_string
"%s <%s> %s %+05d" % [author.name, author.email, authored_date.to_i, 800]
end

def to_hash def to_hash
{ {
'id' => id, 'id' => id,
Expand Down
2 changes: 2 additions & 0 deletions lib/grit/git-ruby.rb
Expand Up @@ -113,6 +113,8 @@ def file_type(ref)


def blame_tree(commit, path = nil) def blame_tree(commit, path = nil)
begin begin
path = path.to_a.join('/').to_s + '/' if (path && path != '')
path = '' if !path.is_a? String
commits = file_index.last_commits(rev_parse({}, commit), looking_for(commit, path)) commits = file_index.last_commits(rev_parse({}, commit), looking_for(commit, path))
clean_paths(commits) clean_paths(commits)
rescue FileIndex::IndexFileNotFound rescue FileIndex::IndexFileNotFound
Expand Down
13 changes: 10 additions & 3 deletions lib/grit/git-ruby/file_index.rb
Expand Up @@ -22,12 +22,18 @@ class IndexFileNotFound < StandardError
class UnsupportedRef < StandardError class UnsupportedRef < StandardError
end end


class << self
attr_accessor :max_file_size
end

self.max_file_size = 10_000_000 # ~10M

attr_reader :files attr_reader :files


# initializes index given repo_path # initializes index given repo_path
def initialize(repo_path) def initialize(repo_path)
@index_file = File.join(repo_path, 'file-index') @index_file = File.join(repo_path, 'file-index')
if File.file?(@index_file) if File.file?(@index_file) && (File.size(@index_file) < Grit::GitRuby::FileIndex.max_file_size)
read_index read_index
else else
raise IndexFileNotFound raise IndexFileNotFound
Expand All @@ -46,6 +52,7 @@ def count(commit_sha)
commits_from(commit_sha).size commits_from(commit_sha).size
end end


# builds a list of all commits reachable from a single commit
def commits_from(commit_sha) def commits_from(commit_sha)
raise UnsupportedRef if commit_sha.is_a? Array raise UnsupportedRef if commit_sha.is_a? Array


Expand All @@ -69,7 +76,7 @@ def commits_from(commit_sha)
end end


def sort_commits(sha_array) def sort_commits(sha_array)
sha_array.sort { |a, b| @commit_order[b] <=> @commit_order[a] } sha_array.sort { |a, b| @commit_order[b].to_i <=> @commit_order[a].to_i }
end end


# returns files changed at commit sha # returns files changed at commit sha
Expand Down Expand Up @@ -183,4 +190,4 @@ def read_index
end end






6 changes: 6 additions & 0 deletions lib/grit/git-ruby/repository.rb
Expand Up @@ -664,6 +664,7 @@ def git_path(path)
private private


def initloose def initloose
@loaded = []
@loose = [] @loose = []
load_loose(git_path('objects')) load_loose(git_path('objects'))
load_alternate_loose(git_path('objects')) load_alternate_loose(git_path('objects'))
Expand All @@ -675,6 +676,7 @@ def load_alternate_loose(path)
alt = File.join(path, 'info/alternates') alt = File.join(path, 'info/alternates')
if File.exists?(alt) if File.exists?(alt)
File.readlines(alt).each do |line| File.readlines(alt).each do |line|
next if @loaded.include?(line.chomp)
if line[0, 2] == '..' if line[0, 2] == '..'
line = File.expand_path(File.join(@git_dir, line)) line = File.expand_path(File.join(@git_dir, line))
end end
Expand All @@ -685,12 +687,14 @@ def load_alternate_loose(path)
end end


def load_loose(path) def load_loose(path)
@loaded << path
return if !File.exists?(path) return if !File.exists?(path)
@loose << Grit::GitRuby::Internal::LooseStorage.new(path) @loose << Grit::GitRuby::Internal::LooseStorage.new(path)
end end


def initpacks def initpacks
close close
@loaded_packs = []
@packs = [] @packs = []
load_packs(git_path("objects/pack")) load_packs(git_path("objects/pack"))
load_alternate_packs(git_path('objects')) load_alternate_packs(git_path('objects'))
Expand All @@ -705,13 +709,15 @@ def load_alternate_packs(path)
line = File.expand_path(File.join(@git_dir, line)) line = File.expand_path(File.join(@git_dir, line))
end end
full_pack = File.join(line.chomp, 'pack') full_pack = File.join(line.chomp, 'pack')
next if @loaded_packs.include?(full_pack)
load_packs(full_pack) load_packs(full_pack)
load_alternate_packs(File.join(line.chomp)) load_alternate_packs(File.join(line.chomp))
end end
end end
end end


def load_packs(path) def load_packs(path)
@loaded_packs << path
return if !File.exists?(path) return if !File.exists?(path)
Dir.open(path) do |dir| Dir.open(path) do |dir|
dir.each do |entry| dir.each do |entry|
Expand Down
11 changes: 9 additions & 2 deletions lib/grit/git.rb
Expand Up @@ -21,6 +21,13 @@ class << self
self.git_binary = "/usr/bin/env git" self.git_binary = "/usr/bin/env git"
self.git_timeout = 10 self.git_timeout = 10


def self.with_timeout(timeout = 10.seconds)
old_timeout = Grit::Git.git_timeout
Grit::Git.git_timeout = timeout
yield
Grit::Git.git_timeout = old_timeout
end

attr_accessor :git_dir, :bytes_read attr_accessor :git_dir, :bytes_read


def initialize(git_dir) def initialize(git_dir)
Expand All @@ -43,12 +50,12 @@ def method_missing(cmd, options = {}, *args)
end end


def run(prefix, cmd, postfix, options, args) def run(prefix, cmd, postfix, options, args)
timeout = options.delete(:timeout) timeout = options.delete(:timeout) rescue nil
timeout = true if timeout.nil? timeout = true if timeout.nil?


opt_args = transform_options(options) opt_args = transform_options(options)
ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{a}'" } ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{a}'" }

call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{postfix}" call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{postfix}"
Grit.log(call) if Grit.debug Grit.log(call) if Grit.debug
response, err = timeout ? sh(call) : wild_sh(call) response, err = timeout ? sh(call) : wild_sh(call)
Expand Down
9 changes: 1 addition & 8 deletions lib/grit/index.rb
Expand Up @@ -64,14 +64,7 @@ def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master'


commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit') commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')


# self.repo.git.update_ref({}, 'HEAD', commit_sha1) self.repo.update_ref(head, commit_sha1)
ref_heads = File.join(self.repo.path, 'refs', 'heads')
FileUtils.mkdir_p(ref_heads)
File.open(File.join(ref_heads, head), 'w') do |f|
f.write(commit_sha1)
end if commit_sha1

commit_sha1
end end


# Recursively write a tree to the index # Recursively write a tree to the index
Expand Down
45 changes: 45 additions & 0 deletions lib/grit/merge.rb
@@ -0,0 +1,45 @@
module Grit

class Merge

STATUS_BOTH = 'both'
STATUS_OURS = 'ours'
STATUS_THEIRS = 'theirs'

attr_reader :conflicts, :text, :sections

def initialize(str)
status = STATUS_BOTH

section = 1
@conflicts = 0
@text = {}

lines = str.split("\n")
lines.each do |line|
if /^<<<<<<< (.*?)/.match(line)
status = STATUS_OURS
@conflicts += 1
section += 1
elsif line == '======='
status = STATUS_THEIRS
elsif /^>>>>>>> (.*?)/.match(line)
status = STATUS_BOTH
section += 1
else
@text[section] ||= {}
@text[section][status] ||= []
@text[section][status] << line
end
end
@text = @text.values
@sections = @text.size
end

# Pretty object inspection
def inspect
%Q{#<Grit::Merge}
end
end # Merge

end # Grit
36 changes: 34 additions & 2 deletions lib/grit/repo.rb
Expand Up @@ -51,6 +51,11 @@ def self.init(path)
def description def description
File.open(File.join(self.path, 'description')).read.chomp File.open(File.join(self.path, 'description')).read.chomp
end end

def blame(file, commit = nil)
Blame.new(self, file, commit)
end



# An array of Head objects representing the branch heads in # An array of Head objects representing the branch heads in
# this repo # this repo
Expand All @@ -62,10 +67,14 @@ def heads


alias_method :branches, :heads alias_method :branches, :heads


def is_head?(head_name) def get_head(head_name)
heads.find { |h| h.name == head_name } heads.find { |h| h.name == head_name }
end end


def is_head?(head_name)
get_head(head_name)
end

# Object reprsenting the current repo head. # Object reprsenting the current repo head.
# #
# Returns Grit::Head (baked) # Returns Grit::Head (baked)
Expand Down Expand Up @@ -320,7 +329,6 @@ def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz
self.git.archive(options, treeish, "| gzip > #{filename}") self.git.archive(options, treeish, "| gzip > #{filename}")
end end



# Enable git-daemon serving of this repository by writing the # Enable git-daemon serving of this repository by writing the
# git-daemon-export-ok file to its git directory # git-daemon-export-ok file to its git directory
# #
Expand Down Expand Up @@ -378,6 +386,30 @@ def index
Index.new(self) Index.new(self)
end end


def update_ref(head, commit_sha)
return nil if !commit_sha || (commit_sha.size != 40)

# check packed refs - remove head if it's there
packed_refs = File.join(self.path, 'packed-refs')
if File.file?(packed_refs)
pr = File.read(packed_refs)
refs = pr.split("\n")
refs = refs.reject { |ref| /[0-9a-z]{40} refs\/heads\/#{head}/.match(ref) }
pr = refs.join("\n")
File.open(packed_refs, 'w') do |f|
f.write(pr)
end
end

ref_heads = File.join(self.path, 'refs', 'heads')
FileUtils.mkdir_p(ref_heads)
File.open(File.join(ref_heads, head), 'w') do |f|
f.write(commit_sha)
end
commit_sha

end

# Pretty object inspection # Pretty object inspection
def inspect def inspect
%Q{#<Grit::Repo "#{@path}">} %Q{#<Grit::Repo "#{@path}">}
Expand Down

0 comments on commit 1c002dd

Please sign in to comment.