class Gash

What is Gash?

Some of these “rules” might change it the future.

How do you install it?

The stable version can installed through RubyGems:

sudo gem install gash

The unstable version can be checked out through Git at GitHub, and installed through this command:

rake install

How do you use it?

gash = Gash.new
gash["README"] = "new content"
gash.commit("Some changes...")

It’s also important to remember that a Gash is simply a Tree, so you can also call those methods.

See also: new, commit, Tree

Credits

This code is based upon git-shelve, created by Michael Siebert, which is released under LGPL. However, Michael has allowed me to release this under the MIT-license as long as I keep his name here.

And, in fact: I could never create this without the code written by Michael. You should really thank him!

Older versions of Gash, which doesn’t include this section or the MIT-license, is still licensed under LGPL.

Attributes

branch[RW]
repository[RW]

Public Class Methods

new(repo = ".", branch = "master") click to toggle source

Opens the repo with the specified branch.

# File lib/gash.rb, line 350
def initialize(repo = ".", branch = "master")
  @branch = branch
  @repository = repo
  @repository = find_repo(repo)
  __setobj__(Tree.new(:parent => self))
  update!
end

Public Instance Methods

branch_exists?() click to toggle source

Checks if the current branch exists

# File lib/gash.rb, line 399
def branch_exists?
  git_status('rev-parse', @branch) == 0
end
cat_file(blob) click to toggle source
# File lib/gash.rb, line 418
def cat_file(blob)
  git('cat-file', 'blob', blob)
end
commit(msg) click to toggle source

Commit the current changes and returns the commit-hash.

Returns nil if nothing has changed.

# File lib/gash.rb, line 391
def commit(msg)
  return unless changed?
  commit = commit_tree(to_tree!, msg)
  @sha1 = git_tree_sha1
  commit
end
commit_tree(tree, msg) click to toggle source
# File lib/gash.rb, line 442
def commit_tree(tree, msg)
  if branch_exists?
    commit = git('commit-tree', tree, '-p', @branch, :input => msg)
    update_head(commit)
  else
    commit = git('commit-tree', tree, :input => msg)
    git('branch', @branch, commit)
  end
  commit
end
find_repo(dir) click to toggle source

private

# File lib/gash.rb, line 410
def find_repo(dir)
  Dir.chdir(dir) do
    File.expand_path(git('rev-parse', '--git-dir', :git_dir => false))
  end
rescue Errno::ENOENT, Gash::Errors::Git
  raise Errors::NoGitRepo.new("No Git repository at: " + @repository)
end
git(cmd, *rest, &block) click to toggle source

passes the command over to git

Parameters

cmd<String>

the git command to execute

*rest

any number of String arguments to the command, followed by an options hash

&block

if you supply a block, you can communicate with git throught a pipe. NEVER even think about closing the stream!

Options

:strip<Boolean>

true to strip the output String#strip, false not to to it

Raises

Errors::Git

if git returns non-null, an Exception is raised

Returns

String

if you didn’t supply a block, the things git said on STDOUT, otherwise noting

# File lib/gash.rb, line 487
def git(cmd, *rest, &block)
  result, reserr, status = run_git(cmd, *rest, &block)

  if status != 0
    raise Errors::Git.new("Error: #{cmd} returned #{status}. STDERR: #{reserr}")
  end
  result
end
git_status(cmd, *rest, &block) click to toggle source

passes the command over to git and returns its status ($?)

Parameters

cmd<String>

the git command to execute

*rest

any number of String arguments to the command, followed by an options hash

&block

if you supply a block, you can communicate with git throught a pipe. NEVER even think about closing the stream!

Returns

Integer

the return status of git

# File lib/gash.rb, line 506
def git_status(cmd, *rest, &block)
  run_git(cmd, *rest, &block)[2]
end
git_tree(&blk) click to toggle source
# File lib/gash.rb, line 453
def git_tree(&blk)
  git('ls-tree', '-r', '-t', '-z', @branch).split("\0").each(&blk)
rescue Errors::Git
  ""
end
git_tree_sha1(from = @branch) click to toggle source
# File lib/gash.rb, line 459
def git_tree_sha1(from = @branch)
  git('rev-parse', @branch + '^{tree}')
rescue Errors::Git
end
method_missing(meth, *args, &blk) click to toggle source
# File lib/gash.rb, line 464
def method_missing(meth, *args, &blk)
  target = self.__getobj__
  unless target.respond_to?(meth)
    Object.instance_method(:method_missing).bind(self).call(meth, *args, &blk)
  end
  target.__send__(meth, *args, &blk)
end
run_git(cmd, *args) { |stdin| ... } click to toggle source

passes the command over to git (you should not call this directly)

Parameters

cmd<String>

the git command to execute

*rest

any number of String arguments to the command, followed by an options hash

&block

if you supply a block, you can communicate with git throught a pipe. NEVER even think about closing the stream!

Options

:strip<Boolean>

true to strip the output String#strip, false not to to it

:git_dir<Boolean>

true to automatically use @repository as git-dir, false to not use anything.

Raises

Errors::Git

if git returns non-null, an Exception is raised

Returns

Array[String, String, Integer]

the first item is the STDOUT of git, the second is the STDERR, the third is the return-status

# File lib/gash.rb, line 526
def run_git(cmd, *args, &block)
  options = if args.last.kind_of?(Hash)
    args.pop
  else
    {}
  end
  options[:strip] = true unless options.key?(:strip)
  
  git_cmd = ["git"]
  
  unless options[:git_dir] == false
    git_cmd.push("--git-dir", @repository)
  end

  git_cmd.push(cmd, *args)
  
  result = ""
  reserr = ""
  status = Open4.popen4(*git_cmd) do |pid, stdin, stdout, stderr|
    if input = options.delete(:input)
      stdin.write(input.join)
    elsif block_given?
      yield stdin
    end
    stdin.close_write

    result = ""
    reserr = ""

    while !stdout.eof
      result << stdout.read
    end
    
    while !stderr.eof
      reserr << stderr.read
    end
  end

  result.strip! if options[:strip] == true
  
  [result, reserr, status]
end
to_tree!(from = self) click to toggle source
# File lib/gash.rb, line 422
def to_tree!(from = self)
  input = []
  from.each do |key, value|
    if value.tree?
      value.sha1 ||= to_tree!(value)
      value.mode ||= "040000"
      input << "#{value.mode} tree #{value.sha1}\t#{key}\0"
    else
      value.sha1 ||= git('hash-object', '-w', '--stdin', :input => value.to_s)
      value.mode ||= "100644" 
      input << "#{value.mode} blob #{value.sha1}\t#{key}\0"
    end
  end
  git('mktree', '-z', :input => input)
end
update!() click to toggle source

Fetch the latest data from Git; you can use this as a clear-method.

# File lib/gash.rb, line 363
def update!
  clear
  self.sha1 = git_tree_sha1
  git_tree do |line|
    line.strip!
    mode = line[0, 6]
    type = line[7]
    sha1 = line[12, 40]
    name = line[53..-1]
    name = name[/[^\/]+$/]
    parent = if $`.empty?
      self
    else
      self[$`.chomp("/")]
    end
    parent[name, true] = case type
    when ?b
      Blob.new(:sha1 => sha1, :mode => mode)
    when ?t
      Tree.new(:sha1 => sha1, :mode => mode)
    end
  end
  self
end
update_head(new_head) click to toggle source
# File lib/gash.rb, line 438
def update_head(new_head)
  git('update-ref', 'refs/heads/%s' % @branch, new_head)
end