class GitDS::Repo

A Git repository.

Note: StagingIndex is cached, as it is from the command line.

Constants

DEFAULT_BRANCH
DEFAULT_TAG
GIT_DIR

Attributes

current_branch[R]
last_branch_tag[R]
path[R]

Public Class Methods

create(path) click to toggle source

TODO: something more intelligent, e.g. with git/repo options

# File lib/git-ds/repo.rb, line 43
def self.create(path)
  Grit::Repo.init(path)

  # handle broken Grit init
  if not File.exist?(path + GIT_DIR)
    `git init #{path}`
  end

  self.new(path)          # discard Grit::Repo object and use a Repo object
end
new(path) click to toggle source

Initialize a repo from the .git subdir in the given path.

Calls superclass method
# File lib/git-ds/repo.rb, line 76
def initialize(path)
  path.chomp(GIT_DIR) if path.end_with? GIT_DIR
  @path = (path.empty?) ? '.' : path

  # TODO: get last branch tag from repo
  #       prob as a git-config
  @last_branch_tag = DEFAULT_TAG.dup
  @current_branch = DEFAULT_BRANCH.dup
  @staging_index = nil
  @saved_stages = {}

  super(@path + GIT_DIR)
end
top_level(path='.') click to toggle source

Return the top-level directory of the repo containing the location ‘path’.

# File lib/git-ds/repo.rb, line 57
def self.top_level(path='.')
  local = (path == '.')
  old_dir = nil

  if path != '.'
    old_dir = Dir.getwd
    dest = (File.directory? path) ? path : File.dirname(path)
    Dir.chdir dest
  end

  dir = `git rev-parse --show-toplevel`.chomp
  Dir.chdir old_dir if old_dir

  (dir == '.git') ? '.' : dir.chomp(GIT_DIR) 
end

Public Instance Methods

add(path, data='', on_fs=false) click to toggle source

Add a DB entry at the filesystem path ‘path’ with contents ‘data’. If ‘on_fs’ is true, the file is created in the filesystem as well. This uses the staging index.

# File lib/git-ds/repo.rb, line 328
def add(path, data='', on_fs=false)
  self.stage { |idx| idx.add(path, data, on_fs) }
end
Also aliased as: add_files
add_files(path, data='', on_fs=false)

Alias for: add
branch(tag=@current_branch) click to toggle source

Return the Head object for the specified branch

# File lib/git-ds/repo.rb, line 126
def branch(tag=@current_branch)
  get_head(tag)
end
branch=( tag, actor=nil )
Alias for: set_branch
clean_tag(name) click to toggle source

# ———————————————————————- Return a cleaned-up version of the tag name, suitable for use as a filename. Replaces all non-alphanumeric characters (except “-.,”) with “_”.

# File lib/git-ds/repo.rb, line 111
def clean_tag(name)
  name.gsub( /[^-.,_[:alnum:]]/, '_' )
end
create_branch( tag=next_branch_tag(), sha=nil ) click to toggle source

Creates a branch in refs/heads and associates it with the specified commit. If sha is nil, the latest commit from ‘master’ is used. The canonical name of the tag is returned.

# File lib/git-ds/repo.rb, line 135
def create_branch( tag=next_branch_tag(), sha=nil )
  if not sha
    sha = commits.last.id
    #sha = branches.first.commit.id if not sha
  end
  name = clean_tag(tag)
  update_ref(name, sha)
  name
end
delete(path) click to toggle source

Remove an object from the database. This can be a path to a Tree or a Blob.

# File lib/git-ds/repo.rb, line 335
def delete(path)
  self.stage { |idx| idx.delete(path) }
end
exec_git_cmd( cmd, actor=nil ) click to toggle source

Execute the specified command using Repo#exec_in_git_dir.

# File lib/git-ds/repo.rb, line 290
def exec_git_cmd( cmd, actor=nil )
  old_aname = ENV['GIT_AUTHOR_NAME']
  old_aemail = ENV['GIT_AUTHOR_EMAIL']
  old_cname = ENV['GIT_COMMITTER_NAME']
  old_cemail = ENV['GIT_COMMITTER_EMAIL']
  old_pager = ENV['GIT_PAGER']

  if actor
    ENV['GIT_AUTHOR_NAME'] = actor.name
    ENV['GIT_AUTHOR_EMAIL'] = actor.email
    ENV['GIT_COMMITTER_NAME'] = actor.name
    ENV['GIT_COMMITTER_EMAIL'] = actor.email
  end
  ENV['GIT_PAGER'] = ''

  # Note: we cannot use Grit#raw_git_call as it requires an index file
  rv = exec_in_git_dir do 
    `#{cmd}`
    raise CommandError, rv if $? != 0
  end

  ENV['GIT_AUTHOR_NAME'] = old_aname
  ENV['GIT_AUTHOR_EMAIL'] = old_aemail
  ENV['GIT_COMMITTER_NAME'] = old_cname
  ENV['GIT_COMMITTER_EMAIL'] = old_cemail
  ENV['GIT_PAGER'] = old_pager

  rv
end
exec_in_git_dir() { || ... } click to toggle source

# ———————————————————————- Change to the Repo#top_level dir, yield to block, then pop the dir stack.

# File lib/git-ds/repo.rb, line 273
def exec_in_git_dir(&block)
  curr = Dir.getwd
  result = nil
  begin
    Dir.chdir top_level
    result = yield
  rescue
    raise
  ensure
    Dir.chdir curr
  end
  result
end
exist?(path)
Alias for: include?
include?(path) click to toggle source

Return true if path exists in repo (on fs or in-tree)

# File lib/git-ds/repo.rb, line 100
def include?(path)
  path_to_object(path) ? true : false
end
Also aliased as: exist?
index()
Alias for: staging
index=(idx)
Alias for: staging=
index_new() click to toggle source

Return an empty git index for the repo.

# File lib/git-ds/repo.rb, line 204
def index_new
  Index.new(self)
end
list(path=nil) click to toggle source

Return a Hash of the contents of ‘path’. This is just a wrapper for tree_contents.

# File lib/git-ds/repo.rb, line 422
def list(path=nil)
  sha = path_to_sha(path)
  # ensure correct operation even if path doesn't exist in repo
  t = sha ? tree(sha) : tree(@current_branch, (path ? [path] : []))
  t ? tree_contents( t ) : {}
end
list_blobs(path='') click to toggle source

Return Hash of all Blob child objects at ‘path’.

# File lib/git-ds/repo.rb, line 432
def list_blobs(path='')
  list(path).delete_if { |k,v| not v.kind_of?(Grit::Blob) }
end
list_trees(path='') click to toggle source

Return Hash of all Tree child objects at ‘path’.

# File lib/git-ds/repo.rb, line 439
def list_trees(path='')
  list(path).delete_if { |k,v| not v.kind_of?(Grit::Tree) }
end
merge_branch( tag=@current_branch, actor=nil ) click to toggle source

Merge specified branch into master.

# File lib/git-ds/repo.rb, line 172
def merge_branch( tag=@current_branch, actor=nil )
  raise "Invalid branch '#{tag}'" if not is_head?(tag)

  tag.gsub!(/['\\]/, '')

  # switch to master branch
  set_branch(DEFAULT_BRANCH, actor)

  # merge target branch to master branch

  rv = nil
  begin
    rv = exec_git_cmd("git merge -n --no-ff --no-log --no-squash '#{tag}'", 
                      actor)
  rescue CommandError => e
    $stderr.puts e.message
  end
  rv
end
next_branch_tag() click to toggle source

Returns the next value for a tag. This is primarily used to auto-generate tag names, e.g. 1.0.1, 1.0.2, etc.

# File lib/git-ds/repo.rb, line 119
def next_branch_tag
  @last_branch_tag.succ!
end
object_data(path) click to toggle source

Fetch the contents of a DB or FS object from the object database. This uses the staging index. Note: This returns nil if path is a Tree instead of a blob

# File lib/git-ds/repo.rb, line 344
def object_data(path)
  blob = path_to_object(path)
  return nil if (not blob) || (blob.kind_of? Grit::Tree)
  blob.kind_of?(Grit::Blob) ? blob.data : blob
end
path_to_object(path) click to toggle source

Fetch an object from the repo based on its path.

If a staging index exists, its tree will be used; otherwise, the tree Repo#current_branch will be used.

The object returned will be a Grit::Blob, a Grit::Tree, or the raw data from the staging index.

# File lib/git-ds/repo.rb, line 463
def path_to_object(path)
  treeish = (@staging_index ? staging.sha : @current_branch)
  tree = self.tree(treeish, [path])
  return tree.blobs.first if tree && (not tree.blobs.empty?)
  return tree.trees.first if tree && (not tree.trees.empty?)

  # check staging index in case object has not been written to object DB
  staging? ? staging.path_to_object(path) : nil
end
path_to_sha(path, head=@current_branch) click to toggle source

Return the SHA1 of ‘path’ in repo. Uses staging index if present.

# File lib/git-ds/repo.rb, line 382
def path_to_sha(path, head=@current_branch)
  # Return the root of the repo if no path is specified
  return root_sha(head) if (not path) || (path.empty?)

  if staging?
    @staging_index.sync
    head = @staging_index.current_tree.id
  end

  dir = tree(head, [path])
  (dir && dir.contents.length > 0) ? dir.contents.first.id : nil
end
raw_tree(path, recursive=false) click to toggle source

Returns the raw (git cat-file) representation of a tree.

# File lib/git-ds/repo.rb, line 446
def raw_tree(path, recursive=false)
  # Note: to construct recursive tree:
  #   Tree.allocate.construct_initialize(repo, treeish, output)
  #   from repo.git.ls_tree or raw_tree
  sha = path_to_sha(path)
  sha ? git.ruby_git.get_raw_tree( sha, recursive ) : ''
end
root_sha(head=@current_branch) click to toggle source

Return the SHA of the root Tree in the repository.

Uses the staging index if it is active.

# File lib/git-ds/repo.rb, line 400
def root_sha(head=@current_branch)
  if staging?
    @staging_index.sync
    return @staging_index.sha
  end

  (self.commits.count > 0) ? self.commits.last.tree.id : nil
end
set_branch( tag, actor=nil ) click to toggle source

Sets the current branch to the specified tag. This changes the default branch for all repo activity and sets HEAD.

# File lib/git-ds/repo.rb, line 149
def set_branch( tag, actor=nil )
  # allow creating of new branches via -b if they do not exist
  opt = (is_head? tag) ? '' : '-b'

  # Save staging index for current branch
  @saved_stages[@current_branch] = self.staging if staging?

  exec_git_cmd( "git checkout -q -m #{opt} '#{tag}'", actor )

  # Synchronize staging index (required before merge)
  unstage

  # Update current_branch info and restore staging for branch
  self.staging = @saved_stages[tag]
  self.staging.sync if staging?
  @current_branch = tag
end
Also aliased as: branch=
stage() { |idx| ... } click to toggle source

Yield staging index to the provided block, then write the index when the block returns. This allows the Git staging index to be modified from within Ruby, with all changes being visible to the Git command-line tools. Returns staging index for chaining purposes.

# File lib/git-ds/repo.rb, line 250
def stage(&block)
  idx = self.staging
  rv = yield idx
  idx.build
  rv
end
stage_and_commit(msg, actor=nil, &block) click to toggle source

Read the Git staging index, then commit it with the provided message and author info. Returns SHA of commit.

# File lib/git-ds/repo.rb, line 262
def stage_and_commit(msg, actor=nil, &block)
  stage(&block)
  sha = staging.commit(msg, actor)
  unstage
  sha
end
staging() click to toggle source

Return the staging index for the repo.

# File lib/git-ds/repo.rb, line 211
def staging
  # TODO: mutex
  @staging_index ||= StageIndex.read(self)
end
Also aliased as: index
staging=(idx) click to toggle source

Set the staging index. This can be used to clear the staging index, or to use a specific index as the staging index.

# File lib/git-ds/repo.rb, line 220
def staging=(idx)
  # TODO: mutex
  @staging_index = idx
end
Also aliased as: index=
staging?() click to toggle source

Return true if a staging index is active.

# File lib/git-ds/repo.rb, line 236
def staging?
  @staging_index != nil
end
tag_object(tag, sha) click to toggle source

Tag (name) an object, e.g. a commit.

# File lib/git-ds/repo.rb, line 195
def tag_object(tag, sha)
  git.fs_write("refs/tags/#{clean_tag(tag)}", sha)
end
top_level() click to toggle source

Return the top-level directory of the repo (the parent of .git).

# File lib/git-ds/repo.rb, line 93
def top_level
  git.git_dir.chomp(GIT_DIR)
end
tree(treeish=nil, paths = []) click to toggle source

# ———————————————————————- The Tree object for the given treeish reference

+treeish+ is the reference (default Repo#current_branch)
+paths+ is an optional Array of directory paths to restrict the tree (default [])

Uses staging index if present and provides wrapper for nil treeish.

Examples 
 repo.tree('master', ['lib/'])

Returns Grit::Tree (baked)
Calls superclass method
# File lib/git-ds/repo.rb, line 363
def tree(treeish=nil, paths = [])
  begin 
    if staging? && (not treeish)
      @staging_index.sync
      super(@staging_index.current_tree.id, paths)
    else
      treeish = @current_branch if not treeish
      super
    end
  rescue Grit::GitRuby::Repository::NoSuchPath
    
  end
end
tree_contents(tree) click to toggle source

Return a Hash of the contents of ‘tree’. The key is the filename, the value is the Grit object (a Tree or a Blob).

# File lib/git-ds/repo.rb, line 413
def tree_contents(tree)
  return {} if not tree
  tree.contents.inject({}) { |h,item| h[item.name] = item; h }
end
unstage() click to toggle source

Close staging index. This discards all (non-committed) changes in the staging index.

# File lib/git-ds/repo.rb, line 229
def unstage
  self.staging=(nil)    # yes, the self is required. not sure why.
end