Skip to content

Commit

Permalink
Merge branch 'gitfactor'
Browse files Browse the repository at this point in the history
  • Loading branch information
bronson committed Nov 2, 2011
2 parents 9ab5c16 + e4253cc commit a153c2f
Show file tree
Hide file tree
Showing 7 changed files with 478 additions and 127 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -21,6 +21,10 @@ gem 'i18n' ,'~> 0.4.1'
gem 'retryable' , :git => 'git://github.com/bronson/retryable.git'
gem 'gitrb' , :git => 'git://github.com/bronson/gitrb.git', :branch => 'patch-1'

group :development do
gem 'guard-rspec'
end

group :test do
gem 'rake' # needed by travis-ci
gem 'rspec' ,'~> 2.5'
Expand Down
8 changes: 7 additions & 1 deletion Gemfile.lock
Expand Up @@ -46,6 +46,10 @@ GEM
loofah (>= 0.3.1)
nokogiri (> 0.0.0)
sax-machine (>= 0.0.12)
guard (0.8.8)
thor (~> 0.14.6)
guard-rspec (0.5.1)
guard (>= 0.8.4)
hashie (1.0.0)
hpricot (0.8.2)
htmlentities (4.2.4)
Expand All @@ -72,7 +76,7 @@ GEM
rash (~> 0.3.0)
polyglot (0.3.1)
rack (1.3.0)
rake (0.9.2)
rake (0.9.2.2)
rash (0.3.0)
hashie (~> 1.0.0)
rspec (2.6.0)
Expand All @@ -85,6 +89,7 @@ GEM
rspec-mocks (2.6.0)
sax-machine (0.0.16)
nokogiri (> 0.0.0)
thor (0.14.6)
treetop (1.4.9)
polyglot (>= 0.3.1)

Expand All @@ -96,6 +101,7 @@ DEPENDENCIES
erubis (~> 2.6.6)
feedzirra (~> 0.0.24)
gitrb!
guard-rspec
hashie (~> 1.0)
hpricot (= 0.8.2)
htmlentities (~> 4.2.1)
Expand Down
5 changes: 5 additions & 0 deletions Guardfile
@@ -0,0 +1,5 @@
guard 'rspec', :version => 2, :notification => false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
end

2 changes: 2 additions & 0 deletions TODO
Expand Up @@ -6,6 +6,8 @@ WTF happened to 2387? Its description is now <td>Wisely add </td>?

Also https://github.com/vim-scripts/DBGp-client--Lancien is jacked

Remember last_script_id, even when doing a full scrape. Only set it to a random number
when first setting a scraper up.

push repos/dbext.vim
~/vim-scraper/repos/dbext.vim.git$ git push origin master
Expand Down
160 changes: 127 additions & 33 deletions lib/gitrepo.rb
@@ -1,66 +1,160 @@
# interface for working on git repos, insulates app from underlying implementation

# todo: only call git via array so no shell interp issues
# todo: make a way for caller to tell Repo to indent all messages
require 'gitrb'
require 'retryable'


class GitRepo
include Retryable # only for network operations

class GitError < RuntimeError; end
attr_reader :root, :bare

# required: :root, the directory to contain the repo
# optional: :clone a repo to clone (:bare => true if it should be bare)
# :create to create a new empty repo if it doesn't already exist
def initialize opts
@root = opts[:root]
@bare = opts[:bare]

retryable_options opts[:retryable_options] if opts[:retryable_options]

if opts[:clone]
retryable(:task => "cloning #{opts[:clone]}") do
# todo: add support for :bare
output = `git clone #{opts[:clone]} #{opts[:root]} 2>&1`
raise GitError.new("git clone failed: #{output}") unless $?.success?
args = [opts[:clone], opts[:root]]
args.push '--bare' if opts[:bare]
git_exec :clone, *args
end
elsif opts[:create]
Dir.mkdir opts[:root] unless test ?d, opts[:root]
if opts[:bare]
git :init, '--bare'
else
git :init
end
end

# a little sanity checking, catch simple errors early
raise GitError.new "#{@root} does not exist" unless test ?d, @root
if opts[:bare]
raise GitError.new "#{@root} does not appear to be a bare repo" unless test(?d, File.join(@root, 'objects'))
else
raise "#{@root} doesn't exist" unless test ?d, @root
raise GitError.new "#{@root}/.git does not exist" unless test(?d, File.join(@root, '.git'))
raise GitError.new "#{@root}/.git does not appear to be a git repo" unless test(?d, File.join(@root, '.git', 'objects'))
end
end

def git_exec *args
options = { :before_exec => lambda { } }
options.merge! args.pop if args[-1].is_a? Hash
args = args.map { |a| a.to_s }

out = IO.popen('-', 'r') do |io|
if io # parent
block_given? ? yield(io) : io.read
else # child
STDERR.reopen STDOUT
options[:before_exec].call
exec 'git', *args
end
end

if $?.exitstatus > 0
raise GitError.new("git #{args.join(' ')}: #{out}")
end

out
end

def git *args
Dir.chdir(@root) do
git_exec *args
end
end

# i.e. remote_add 'rails', 'http://github.com/rails/rails.git'
def remote_add name, remote
Dir.chdir(@root) {
output = `git remote add #{name} #{remote} 2>&1`
raise GitError.new("generate_docs: git remote add #{name} failed: #{output}") unless $?.success?
}
git :remote, :add, name, remote
end

# todo: get rid of this call, should be regular git add / git commit
def commit_all message
Dir.chdir(@root) {
output = `git commit -a -m '#{message}' 2>&1`
if output =~ /nothing to commit/
puts " no changes to generated files"
else
raise GitError.new("generate_docs: git commit failed: #{output}") unless $?.success?
end
}
def remote_remove name
git :remote, :rm, name
end


def pull *args
Dir.chdir(@root) do
retryable(:task => "pulling #{args.join ' '}") do
# Can we tell the difference between a network error, which we want to retry,
# and a merge error, which we want to fail immediately?
output = `git pull --no-rebase #{args.join ' '} 2>&1`
raise GitError.new("generate_docs: git pull failed: #{output}") unless $?.success?
end
# Can we tell the difference between a network error, which we want to retry,
# and a merge error, which we want to fail immediately?
retryable(:task => "pulling #{args.join ' '}") do
git :pull, '--no-rebase', *args
end
end

def push *args
Dir.chdir(@root) do
retryable(:task => "pushing #{args.join ' '}") do
output = `git push #{args.join ' '} 2>&1`
raise "generate_docs: git push failed: #{output}" unless $?.success?
end
# like pull, can we tell the difference between a network error and local error?
retryable(:task => "pushing #{args.join ' '}") do
git :push, *args
end
end


def create_tag name, message, committer
# gitrb doesn't handle annotated tags so we call git directly
git :tag, '-a', name, '-m', message, :before_exec => lambda {
ENV['GIT_COMMITTER_NAME'] = committer[:name]
ENV['GIT_COMMITTER_EMAIL'] = committer[:email]
ENV['GIT_COMMITTER_DATE'] = (committer[:date] || Time.now).strftime("%s %z")
}
end

def find_tag tagname
tag = git :tag, '-l', tagname
return !tag || tag =~ /^\s*$/ ? nil : tag.chomp
end


# all the things you can do while committing
class CommitHelper
def initialize repo
@repo = repo
end

# this empties out the commit tree so you can start fresh
def empty_index
entries.each { |name| remove name }
end

def remove name
@repo.root.delete(name).dump
end

def add name, contents
# an empty file is represented by the empty string so contents==nil is an error
raise GitError.new "no data in #{name}: #{contents.inspect}" unless contents
@repo.root[name] = Gitrb::Blob.new(:data => contents)
end

def entries
@repo.root.to_a.map { |name,value| name }
end

# If the entry is a tree, returns an array of the names in the tree.
# If a blob, returns the blob data. Othewise returns nil.
def entry name, type=nil
data = @repo.root[name]
raise GitError.new "type was #{data.type} not #{type}" if type && type != data.type
return data.map { |n,v| n } if data && data.type == :tree
return data.dump if data && data.type == :blob
end
end


def commit message, author, committer=author
author = Gitrb::User.new(author[:name], author[:email], author[:date] || Time.now)
committer = Gitrb::User.new(committer[:name], committer[:email], committer[:date] || Time.now)
# gitrb has a bug where it will complain about frozen strings unless you dup the path
repo = Gitrb::Repository.new(:path => root.dup, :bare => bare, :create => false)
repo.transaction(message, author, committer) do
yield CommitHelper.new(repo)
end
end
end

0 comments on commit a153c2f

Please sign in to comment.