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
Public Class Methods
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
Initialize a repo from the .git subdir in the given path.
# 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
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 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
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
# ———————————————————————- 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
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
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
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
# ———————————————————————- 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
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
Return an empty git index for the repo.
# File lib/git-ds/repo.rb, line 204 def index_new Index.new(self) end
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
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
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 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
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
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
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
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
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
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
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
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
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
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
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
Return true if a staging index is active.
# File lib/git-ds/repo.rb, line 236 def staging? @staging_index != nil end
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
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
# ———————————————————————- 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)
# 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
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
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