Class: Gitgo::Git
- Inherits:
-
Object
- Object
- Gitgo::Git
- Includes:
- Enumerable, Utils
- Defined in:
- lib/gitgo/git.rb,
lib/gitgo/git/tree.rb,
lib/gitgo/git/utils.rb
Overview
A wrapper to a Grit::Repo that allows access and modification of a git repository without checking files out (under most circumstances). The api is patterned after the git command line interface.
Usage
Checkout, add, and commit new content:
git = Git.init("example", :author => "John Doe <[email protected]>")
git.add(
"README" => "New Project",
"lib/project.rb" => "module Project\nend",
"remove_this_file" => "won't be here long...")
git.commit("setup a new project")
Content may be removed as well:
git.rm("remove_this_file")
git.commit("removed extra file")
Now access the content:
git["/"] # => ["README", "lib"]
git["/lib/project.rb"] # => "module Project\nend"
git["/remove_this_file"] # => nil
You can go back in time if you wish:
git.branch = "gitgo^"
git["/remove_this_file"] # => "won't be here long..."
For direct access to the Grit objects, use get:
git.get("/lib").id # => "cad0dc0df65848aa8f3fee72ce047142ec707320"
git.get("/lib/project.rb").id # => "636e25a2c9fe1abc3f4d3f380956800d5243800e"
The Working Tree
Changes to the repo are tracked by an in-memory working tree until being committed. Trees can be thought of as a hash of (path, [:mode, sha]) pairs representing the contents of a directory.
git = Git.init("example", :author => "John Doe <[email protected]>")
git.add(
"README" => "New Project",
"lib/project.rb" => "module Project\nend"
).commit("added files")
git.tree
# => {
# "README" => [:"100644", "73a86c2718da3de6414d3b431283fbfc074a79b1"],
# "lib" => {
# "project.rb" => [:"100644", "636e25a2c9fe1abc3f4d3f380956800d5243800e"]
# }
# }
Trees can be collapsed using reset. Afterwards subtrees are only expanded as needed; before expansion they appear as a [:mode, sha] pair and after expansion they appear as a hash. Symbol paths are used to differentiate subtrees (which can be expanded) from blobs (which cannot be expanded).
git.reset
git.tree
# => {
# "README" => [:"100644", "73a86c2718da3de6414d3b431283fbfc074a79b1"],
# :lib => [:"040000", "cad0dc0df65848aa8f3fee72ce047142ec707320"]
# }
git.add("lib/project/utils.rb" => "module Project\n module Utils\n end\nend")
git.tree
# => {
# "README" => [:"100644", "73a86c2718da3de6414d3b431283fbfc074a79b1"],
# "lib" => {
# "project.rb" => [:"100644", "636e25a2c9fe1abc3f4d3f380956800d5243800e"],
# "project" => {
# "utils.rb" => [:"100644", "c4f9aa58d6d5a2ebdd51f2f628b245f9454ff1a4"]
# }
# }
# }
git.rm("README")
git.tree
# => {
# "lib" => {
# "project.rb" => [:"100644", "636e25a2c9fe1abc3f4d3f380956800d5243800e"],
# "project" => {
# "utils.rb" => [:"100644", "c4f9aa58d6d5a2ebdd51f2f628b245f9454ff1a4"]
# }
# }
# }
The working tree can be compared with the commit tree to produce a list of files that have been added and removed using the status method:
git.status
# => {
# "README" => :rm
# "lib/project/utils.rb" => :add
# }
Tracking, Push/Pull
Git provides limited support for setting a tracking branch and doing push/pull from tracking branches without checking the gitgo branch out. More complicated operations can are left to the command line, where the current branch can be directly manipulated by the git program.
Unlike git (the program), Git (the class) requires the upstream branch setup by ‘git branch –track’ to be an existing tracking branch. As an example, if you were to setup this:
% git branch --track remote/branch
Or equivalently this:
git = Git.init
git.track "remote/branch"
Then Git would assume:
-
the upstream branch is ‘remote/branch’
-
the tracking branch is ‘remotes/remote/branch’
-
the ‘branch.name.remote’ config is ‘remote’
-
the ‘branch.name.merge’ config is ‘refs/heads/branch’
If ever these assumptions are broken, for instance if the gitgo branch is manually set up to track a local branch, methods like pull/push could cause odd failures. To help check:
-
track will raise an error if the upstream branch is not a tracking branch
-
upstream_branch raises an error if the ‘branch.name.merge’ config doesn’t follow the ‘ref/heads/branch’ pattern
-
pull/push raise an error given a non-tracking branch
Under normal circumstances, all these assumptions will be met.
Defined Under Namespace
Constant Summary collapse
- DEFAULT_BRANCH =
The default branch
'gitgo'
- DEFAULT_UPSTREAM_BRANCH =
The default upstream branch for push/pull
'origin/gitgo'
- DEFAULT_WORK_DIR =
The default directory for gitgo-related files
'gitgo'
- DEFAULT_BLOB_MODE =
The default blob mode used for added blobs
'100644'.to_sym
- DEFAULT_TREE_MODE =
The default tree mode used for added trees
'40000'.to_sym
- SHA =
A regexp matching a valid sha sum
/\A[A-Fa-f\d]{40}\z/
- GIT_VERSION =
The minimum required version of git (see Git.version_ok?)
[1,6,4,2]
Instance Attribute Summary collapse
-
#branch ⇒ Object
readonly
The gitgo branch.
-
#default_blob_mode ⇒ Object
The default blob mode for self (see DEFAULT_BLOB_MODE).
-
#default_tree_mode ⇒ Object
The default tree mode for self (see DEFAULT_TREE_MODE).
-
#grit ⇒ Object
readonly
The internal Grit::Repo.
-
#head ⇒ Object
readonly
Returns the sha for the branch.
-
#index_file ⇒ Object
readonly
The path to the temporary index_file.
-
#tree ⇒ Object
readonly
The in-memory working tree tracking any adds and removes.
-
#work_dir ⇒ Object
readonly
The path to the instance working directory.
-
#work_tree ⇒ Object
readonly
The path to the temporary working tree.
Class Method Summary collapse
-
.debug(dev = $stdout) ⇒ Object
Sets up Grit to log to the device for the duration of the block.
-
.init(path = Dir.pwd, options = {}) ⇒ Object
Creates a Git instance for path, initializing the repo if necessary.
-
.version ⇒ Object
Returns the git version as an array of integers like [1,6,4,2].
-
.version_ok? ⇒ Boolean
Checks if the git version is compatible with GIT_VERSION.
Instance Method Summary collapse
-
#[](path, entry = false, committed = false) ⇒ Object
Gets the content for path; either the blob data or an array of content names for a tree.
-
#[]=(path, content = nil) ⇒ Object
Sets content for path.
-
#add(paths) ⇒ Object
Adds a hash of (path, content) pairs (see AGET for valid content).
-
#author ⇒ Object
Returns the configured author (which should be a Grit::Actor, or similar).
-
#author=(input) ⇒ Object
Sets the author.
-
#checkout(branch = self.branch) ⇒ Object
Sets the current branch and updates tree.
-
#clone(path, options = {}) ⇒ Object
Clones self into the specified path and sets up tracking of branch in the new grit.
-
#commit(message, options = {}) ⇒ Object
Commits the in-memory working tree to branch with the specified message and returns the sha for the new commit.
-
#commit!(message, options = {}) ⇒ Object
Same as commit but does not check if there are changes to commit, useful when you know there are changes to commit and don’t want the overhead of checking for changes.
-
#commit_grep(pattern, treeish = head) ⇒ Object
Greps for commits with messages matching the pattern, starting at the specified treeish.
-
#diff_tree(a, b = "^#{a}") ⇒ Object
Retuns an array of added, deleted, and modified files keyed by ‘A’, ‘D’, and ‘M’ respectively.
-
#fetch(remote = self.remote) ⇒ Object
Fetches from the remote.
-
#fsck ⇒ Object
Performs ‘git fsck’ and returns the output.
-
#gc ⇒ Object
Performs ‘git gc’ and resets self so that grit will use the updated pack files.
-
#get(type, id) ⇒ Object
Gets the specified object, returning an instance of the appropriate Grit class.
-
#grep(pattern, treeish = head) ⇒ Object
Greps for paths matching the pattern, at the specified treeish.
-
#initialize(path = Dir.pwd, options = {}) ⇒ Git
constructor
Initializes a new Git bound to the repository at the specified path.
-
#ls_tree(treeish) ⇒ Object
Returns an array of paths at the specified treeish.
-
#merge(treeish = upstream_branch) ⇒ Object
Merges the specified reference with the current branch, fast-forwarding when possible.
-
#merge?(treeish = upstream_branch) ⇒ Boolean
Returns true if a merge update is available for branch.
-
#path(*segments) ⇒ Object
Returns the specified path relative to the git repository (ie the .git directory).
-
#prune ⇒ Object
Peforms ‘git prune’ and returns self.
-
#pull(tracking_branch = upstream_branch) ⇒ Object
Fetches the tracking branch and merges with branch.
-
#push(tracking_branch = upstream_branch) ⇒ Object
Pushes branch to the tracking branch.
-
#remote ⇒ Object
Returns the remote as setup by track, or origin if tracking has not been setup.
-
#reset(full = false) ⇒ Object
Resets the working tree.
-
#resolve(id) ⇒ Object
Returns a full sha for the identifier, as determined by rev_parse.
-
#rev_list(*treeishs) ⇒ Object
Returns an array of revisions (commits) reachable from the treeish.
-
#rev_parse(*args) ⇒ Object
Returns an array of shas identified by the args (ex a sha, short-sha, or treeish).
-
#rm(*paths) ⇒ Object
Removes the content at each of the specified paths.
-
#safe_rev_parse(*args) ⇒ Object
Same as rev_parse but always returns an array.
-
#sandbox ⇒ Object
Creates and sets a work tree and index file so that git will have an environment it can work in.
-
#set(type, content) ⇒ Object
Sets an object of the specified type into the git repository and returns the object sha.
-
#stats ⇒ Object
Returns a hash of repo statistics parsed from ‘git count-objects –verbose’.
-
#status(full = false) ⇒ Object
Returns a hash of (path, state) pairs indicating paths that have been added or removed.
-
#track(upstream_branch) ⇒ Object
Sets branch to track the specified upstream_branch.
-
#tracking_branch?(ref) ⇒ Boolean
Returns true if the specified ref is a tracking branch, ie it is the name of an existing remote ref.
-
#tree_grep(pattern, treeish = head) ⇒ Object
Greps for trees matching the pattern, at the specified treeish.
-
#type(sha) ⇒ Object
Returns the type of the object identified by sha; the output of:.
-
#upstream_branch ⇒ Object
Returns the upstream_branch as setup by track.
Methods included from Utils
nil_or_empty?, nil_or_empty_string?, split, with_env
Constructor Details
#initialize(path = Dir.pwd, options = {}) ⇒ Git
Initializes a new Git bound to the repository at the specified path. Raises an error if no such repository exists. Options can specify the following:
:branch the branch for self
:author the author for self
+ any Grit::Repo options
255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/gitgo/git.rb', line 255 def initialize(path=Dir.pwd, ={}) @grit = path.kind_of?(Grit::Repo) ? path : Grit::Repo.new(path, ) @sandbox = false @branch = nil @work_dir = path([:work_dir] || DEFAULT_WORK_DIR) @work_tree = [:work_tree] || File.join(work_dir, 'tmp', object_id.to_s) @index_file = [:index_file] || File.join(work_dir, 'tmp', "#{object_id}.index") self. = [:author] || nil self.checkout [:branch] || DEFAULT_BRANCH self.default_blob_mode = [:default_blob_mode] || DEFAULT_BLOB_MODE self.default_tree_mode = [:default_tree_mode] || DEFAULT_TREE_MODE end |
Instance Attribute Details
#branch ⇒ Object (readonly)
The gitgo branch
224 225 226 |
# File 'lib/gitgo/git.rb', line 224 def branch @branch end |
#default_blob_mode ⇒ Object
The default blob mode for self (see DEFAULT_BLOB_MODE)
242 243 244 |
# File 'lib/gitgo/git.rb', line 242 def default_blob_mode @default_blob_mode end |
#default_tree_mode ⇒ Object
The default tree mode for self (see DEFAULT_TREE_MODE)
245 246 247 |
# File 'lib/gitgo/git.rb', line 245 def default_tree_mode @default_tree_mode end |
#grit ⇒ Object (readonly)
The internal Grit::Repo
221 222 223 |
# File 'lib/gitgo/git.rb', line 221 def grit @grit end |
#head ⇒ Object (readonly)
Returns the sha for the branch
230 231 232 |
# File 'lib/gitgo/git.rb', line 230 def head @head end |
#index_file ⇒ Object (readonly)
The path to the temporary index_file
239 240 241 |
# File 'lib/gitgo/git.rb', line 239 def index_file @index_file end |
#tree ⇒ Object (readonly)
The in-memory working tree tracking any adds and removes
227 228 229 |
# File 'lib/gitgo/git.rb', line 227 def tree @tree end |
#work_dir ⇒ Object (readonly)
The path to the instance working directory
233 234 235 |
# File 'lib/gitgo/git.rb', line 233 def work_dir @work_dir end |
#work_tree ⇒ Object (readonly)
The path to the temporary working tree
236 237 238 |
# File 'lib/gitgo/git.rb', line 236 def work_tree @work_tree end |
Class Method Details
.debug(dev = $stdout) ⇒ Object
Sets up Grit to log to the device for the duration of the block. Primarily useful during debugging, and inadvisable otherwise because the logger must be shared among all Grit::Repo instances (this is a consequence of how logging is implemented in Grit).
170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/gitgo/git.rb', line 170 def debug(dev=$stdout) current_logger = Grit.logger current_debug = Grit.debug begin Grit.logger = Logger.new(dev) Grit.debug = true yield ensure Grit.logger = current_logger Grit.debug = current_debug end end |
.init(path = Dir.pwd, options = {}) ⇒ Object
Creates a Git instance for path, initializing the repo if necessary.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/gitgo/git.rb', line 148 def init(path=Dir.pwd, ={}) unless File.exists?(path) FileUtils.mkdir_p(path) Dir.chdir(path) do = [:is_bare] ? true : false gitdir = || path =~ /\.git$/ ? path : File.join(path, ".git") Utils.with_env('GIT_DIR' => gitdir) do git = Grit::Git.new(gitdir) git.init({:bare => }) end end end new(path, ) end |
.version ⇒ Object
Returns the git version as an array of integers like [1,6,4,2]. The version query performed once and then cached.
185 186 187 |
# File 'lib/gitgo/git.rb', line 185 def version @version ||= `git --version`.split(/\s/).last.split(".").collect {|i| i.to_i} end |
.version_ok? ⇒ Boolean
Checks if the git version is compatible with GIT_VERSION. This check is performed once and then cached.
191 192 193 |
# File 'lib/gitgo/git.rb', line 191 def version_ok? @version_ok ||= ((GIT_VERSION <=> version) <= 0) end |
Instance Method Details
#[](path, entry = false, committed = false) ⇒ Object
Gets the content for path; either the blob data or an array of content names for a tree. Returns nil if path doesn’t exist.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/gitgo/git.rb', line 356 def [](path, entry=false, committed=false) tree = committed ? commit_tree : @tree segments = split(path) unless basename = segments.pop return entry ? tree : tree.keys end unless tree = tree.subtree(segments) return nil end obj = tree[basename] return obj if entry case obj when Array then get(:blob, obj[1]).data when Tree then obj.keys else nil end end |
#[]=(path, content = nil) ⇒ Object
Sets content for path. The content can either be:
-
a string of content
-
a symbol sha, translated to [default_blob_mode, sha]
-
an array like [mode, sha]
-
a nil, to remove content
Note that set content is immediately stored in the repo and tracked in the in-memory working tree but not committed until commit is called.
387 388 389 390 391 392 393 394 395 |
# File 'lib/gitgo/git.rb', line 387 def []=(path, content=nil) segments = split(path) unless basename = segments.pop raise "invalid path: #{path.inspect}" end tree = @tree.subtree(segments, true) tree[basename] = convert_to_entry(content) end |
#add(paths) ⇒ Object
Adds a hash of (path, content) pairs (see AGET for valid content).
452 453 454 455 456 457 458 |
# File 'lib/gitgo/git.rb', line 452 def add(paths) paths.each_pair do |path, content| self[path] = content end self end |
#author ⇒ Object
Returns the configured author (which should be a Grit::Actor, or similar). If no author is is currently set, a default author will be determined from the git configurations.
289 290 291 292 293 294 295 |
# File 'lib/gitgo/git.rb', line 289 def @author ||= begin name = grit.config['user.name'] email = grit.config['user.email'] Grit::Actor.new(name, email) end end |
#author=(input) ⇒ Object
Sets the author. The input may be a Grit::Actor, an array like [author, email], a git-formatted author string, or nil.
299 300 301 302 303 304 305 306 |
# File 'lib/gitgo/git.rb', line 299 def (input) @author = case input when Grit::Actor, nil then input when Array then Grit::Actor.new(*input) when String then Grit::Actor.from_string(*input) else raise "could not convert to Grit::Actor: #{input.class}" end end |
#checkout(branch = self.branch) ⇒ Object
Sets the current branch and updates tree.
Checkout does not actually checkout any files unless a block is given. In that case, the current branch will be checked out for the duration of the block into work_tree; a gitgo-specific directory distinct from the user’s working directory. Checkout with a block permits the execution of git commands that must be performed in a working directory.
Returns self.
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
# File 'lib/gitgo/git.rb', line 577 def checkout(branch=self.branch) # :yields: working_dir if branch != @branch @branch = branch reset end if block_given? sandbox do |git, work_tree, index_file| git.read_tree({:index_output => index_file}, branch) git.checkout_index({:a => true}) yield(work_tree) end end self end |
#clone(path, options = {}) ⇒ Object
Clones self into the specified path and sets up tracking of branch in the new grit. Clone was primarily implemented for testing; normally clones are managed by the user.
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 |
# File 'lib/gitgo/git.rb', line 675 def clone(path, ={}) with_env do grit.git.clone(, grit.path, path) clone = Grit::Repo.new(path) if [:bare] # bare origins directly copy branch heads without mapping them to # 'refs/remotes/origin/' (see git-clone docs). this maps the branch # head so the bare grit can checkout branch clone.git.remote({}, "add", "origin", grit.path) clone.git.fetch({}, "origin") clone.git.branch({}, "-D", branch) end # sets up branch to track the origin to enable pulls clone.git.branch({:track => true}, branch, "origin/#{branch}") self.class.new(clone, :branch => branch, :author => ) end end |
#commit(message, options = {}) ⇒ Object
Commits the in-memory working tree to branch with the specified message and returns the sha for the new commit. The branch is created if it doesn’t already exist. Options can specify (as symbols):
- tree
-
The sha of the tree this commit points to (default the sha for tree, the in-memory working tree)
- parents
-
An array of shas representing parent commits (default the current commit)
- author
-
A Grit::Actor, or similar representing the commit author (default author)
- authored_date
-
The authored date (default now)
- committer
-
A Grit::Actor, or similar representing the user making the commit (default author)
- committed_date
-
The authored date (default now)
Raises an error if there are no changes to commit.
482 483 484 485 |
# File 'lib/gitgo/git.rb', line 482 def commit(, ={}) raise "no changes to commit" if status.empty? commit!(, ) end |
#commit!(message, options = {}) ⇒ Object
Same as commit but does not check if there are changes to commit, useful when you know there are changes to commit and don’t want the overhead of checking for changes.
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
# File 'lib/gitgo/git.rb', line 490 def commit!(, ={}) now = Time.now sha = .delete(:tree) || tree.write_to(self).at(1) parents = .delete(:parents) || (head ? [head] : []) = [:author] || self. = [:authored_date] || now committer = [:committer] || committed_date = [:committed_date] || now # commit format: #--------------------------------------------------- # tree sha # parent sha # author name <email> time_as_int zone_offset # committer name <email> time_as_int zone_offset # # messsage # #--------------------------------------------------- # Note there is a trailing newline after the message. # lines = [] lines << "tree #{sha}" parents.each do |parent| lines << "parent #{parent}" end lines << "author #{.name} <#{.email}> #{.strftime("%s %z")}" lines << "committer #{committer.name} <#{committer.email}> #{committed_date.strftime("%s %z")}" lines << "" lines << lines << "" @head = set('commit', lines.join("\n")) grit.update_ref(branch, head) head end |
#commit_grep(pattern, treeish = head) ⇒ Object
Greps for commits with messages matching the pattern, starting at the specified treeish. Each matching commit yielded to the block.
Instead of a pattern, a hash of git-log options may be provided.
826 827 828 829 830 831 832 833 834 835 836 837 |
# File 'lib/gitgo/git.rb', line 826 def commit_grep(pattern, treeish=head) # :yields: commit = pattern.respond_to?(:merge) ? pattern.dup : {:grep => pattern} .delete_if {|key, value| nil_or_empty?(value) } [:format] = "%H" sandbox do |git, work_tree, index_file| git.log(, treeish).split("\n").each do |sha| yield grit.commit(sha) end end self end |
#diff_tree(a, b = "^#{a}") ⇒ Object
Retuns an array of added, deleted, and modified files keyed by ‘A’, ‘D’, and ‘M’ respectively.
734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
# File 'lib/gitgo/git.rb', line 734 def diff_tree(a, b="^#{a}") sandbox do |git,w,i| output = git.run('', :diff_tree, '', {:r => true, :name_status => true}, [a, b]) diff = {'A' => [], 'D' => [], 'M' => []} output.split("\n").each do |line| mode, path = line.split(' ', 2) array = diff[mode] or raise "unexpected diff output:\n#{output}" array << path end diff end end |
#fetch(remote = self.remote) ⇒ Object
Fetches from the remote.
595 596 597 598 |
# File 'lib/gitgo/git.rb', line 595 def fetch(remote=self.remote) sandbox {|git,w,i| git.fetch({}, remote) } self end |
#fsck ⇒ Object
Performs ‘git fsck’ and returns the output.
853 854 855 856 857 858 |
# File 'lib/gitgo/git.rb', line 853 def fsck sandbox do |git, work_tree, index_file| stdout, stderr = git.sh("#{Grit::Git.git_binary} fsck") "#{stdout}#{stderr}" end end |
#gc ⇒ Object
Performs ‘git gc’ and resets self so that grit will use the updated pack files. Returns self.
847 848 849 850 |
# File 'lib/gitgo/git.rb', line 847 def gc sandbox {|git,w,i| git.gc } reset(true) end |
#get(type, id) ⇒ Object
Gets the specified object, returning an instance of the appropriate Grit class. Raises an error for unknown types.
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/gitgo/git.rb', line 330 def get(type, id) case type.to_sym when :blob then grit.blob(id) when :tree then grit.tree(id) when :commit then grit.commit(id) when :tag object = grit.git.ruby_git.get_object_by_sha1(id) if object.type == :tag Grit::Tag.new(object.tag, grit.commit(object.object)) else nil end else raise "unknown type: #{type}" end end |
#grep(pattern, treeish = head) ⇒ Object
Greps for paths matching the pattern, at the specified treeish. Each matching path and blob are yielded to the block.
Instead of a pattern, a hash of grep options may be provided. The following options are allowed:
:ignore_case
:invert_match
:fixed_strings
:e
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 |
# File 'lib/gitgo/git.rb', line 767 def grep(pattern, treeish=head) # :yields: path, blob = pattern.respond_to?(:merge) ? pattern.dup : {:e => pattern} .delete_if {|key, value| nil_or_empty?(value) } = .merge!( :cached => true, :name_only => true, :full_name => true ) unless commit = grit.commit(treeish) raise "unknown commit: #{treeish}" end sandbox do |git, work_tree, index_file| git.read_tree({:index_output => index_file}, commit.id) git.grep().split("\n").each do |path| yield(path, (commit.tree / path)) end end self end |
#ls_tree(treeish) ⇒ Object
Returns an array of paths at the specified treeish.
750 751 752 753 754 |
# File 'lib/gitgo/git.rb', line 750 def ls_tree(treeish) sandbox do |git,w,i| git.run('', :ls_tree, '', {:r => true, :name_only => true}, [treeish]).split("\n") end end |
#merge(treeish = upstream_branch) ⇒ Object
Merges the specified reference with the current branch, fast-forwarding when possible. This method does not need to checkout the branch into a working directory to perform the merge.
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
# File 'lib/gitgo/git.rb', line 616 def merge(treeish=upstream_branch) sandbox do |git, work_tree, index_file| des, src = safe_rev_parse(branch, treeish) base = des.nil? ? nil : git.merge_base({}, des, src).chomp("\n") case when base == src break when base == des # fast forward situation grit.update_ref(branch, src) else # todo: add rebase as an option git.read_tree({ :m => true, # merge :i => true, # without a working tree :trivial => true, # only merge if no file-level merges are required :aggressive => true, # allow resolution of removes :index_output => index_file }, base, branch, src) commit!("gitgo merge of #{treeish} into #{branch}", :tree => git.write_tree.chomp("\n"), :parents => [des, src] ) end reset end self end |
#merge?(treeish = upstream_branch) ⇒ Boolean
Returns true if a merge update is available for branch.
601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/gitgo/git.rb', line 601 def merge?(treeish=upstream_branch) sandbox do |git, work_tree, index_file| des, src = safe_rev_parse(branch, treeish) case when src.nil? then false when des.nil? then true else des != src && git.merge_base({}, des, src).chomp("\n") != src end end end |
#path(*segments) ⇒ Object
Returns the specified path relative to the git repository (ie the .git directory). With no arguments path returns the repository path.
281 282 283 284 |
# File 'lib/gitgo/git.rb', line 281 def path(*segments) segments.collect! {|segment| segment.to_s } File.join(grit.path, *segments) end |
#prune ⇒ Object
Peforms ‘git prune’ and returns self.
840 841 842 843 |
# File 'lib/gitgo/git.rb', line 840 def prune sandbox {|git,w,i| git.prune } self end |
#pull(tracking_branch = upstream_branch) ⇒ Object
Fetches the tracking branch and merges with branch. No other branches are fetched. Raises an error if given a non-tracking branch (see the Tracking, Push/Pull notes above).
663 664 665 666 667 668 669 670 |
# File 'lib/gitgo/git.rb', line 663 def pull(tracking_branch=upstream_branch) sandbox do |git, work_tree, index_file| remote, remote_branch = parse_tracking_branch(tracking_branch) git.fetch({}, remote, "#{remote_branch}:remotes/#{tracking_branch}") merge(tracking_branch) end reset end |
#push(tracking_branch = upstream_branch) ⇒ Object
Pushes branch to the tracking branch. No other branches are pushed. Raises an error if given a non-tracking branch (see the Tracking, Push/Pull notes above).
653 654 655 656 657 658 |
# File 'lib/gitgo/git.rb', line 653 def push(tracking_branch=upstream_branch) sandbox do |git, work_tree, index_file| remote, remote_branch = parse_tracking_branch(tracking_branch) git.push({}, remote, "#{branch}:#{remote_branch}") unless head.nil? end end |
#remote ⇒ Object
Returns the remote as setup by track, or origin if tracking has not been setup.
437 438 439 |
# File 'lib/gitgo/git.rb', line 437 def remote grit.config["branch.#{branch}.remote"] || 'origin' end |
#reset(full = false) ⇒ Object
Resets the working tree. Also reinitializes grit if full is specified; this can be useful after operations that change configurations or the cached packs (see gc).
532 533 534 535 536 537 538 539 |
# File 'lib/gitgo/git.rb', line 532 def reset(full=false) @grit = Grit::Repo.new(path, :is_bare => grit.) if full commit = grit.commits(branch, 1).first @head = commit ? commit.sha : nil @tree = commit_tree self end |
#resolve(id) ⇒ Object
Returns a full sha for the identifier, as determined by rev_parse. All valid sha string are returned immediately; there is no guarantee the sha will point to an object currently in the repo.
Returns nil the identifier cannot be resolved to an sha.
313 314 315 316 317 318 |
# File 'lib/gitgo/git.rb', line 313 def resolve(id) case id when SHA, nil then id else rev_parse(id).first end end |
#rev_list(*treeishs) ⇒ Object
Returns an array of revisions (commits) reachable from the treeish.
727 728 729 730 |
# File 'lib/gitgo/git.rb', line 727 def rev_list(*treeishs) return treeishs if treeishs.empty? sandbox {|git,w,i| git.run('', :rev_list, '', {}, treeishs).split("\n") } end |
#rev_parse(*args) ⇒ Object
Returns an array of shas identified by the args (ex a sha, short-sha, or treeish). Raises an error if not all args can be converted into a valid sha.
Note there is no guarantee the resulting shas indicate objects in the repository; not even ‘git rev-parse’ will do that.
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
# File 'lib/gitgo/git.rb', line 701 def rev_parse(*args) return args if args.empty? sandbox do |git,w,i| shas = git.run('', :rev_parse, '', {}, args).split("\n") # Grit::Git#run only makes stdout available, not stderr, and so this # wonky check relies on the fact that git rev-parse will print the # unresolved ref to stdout and quit if it can't succeed. That means # the last printout will not look like a sha in the event of an error. unless shas.last.to_s =~ SHA raise "could not resolve to a sha: #{shas.last}" end shas end end |
#rm(*paths) ⇒ Object
Removes the content at each of the specified paths
461 462 463 464 |
# File 'lib/gitgo/git.rb', line 461 def rm(*paths) paths.each {|path| self[path] = nil } self end |
#safe_rev_parse(*args) ⇒ Object
Same as rev_parse but always returns an array. Arguments that cannot be converted to a valid sha will be represented by nil. This method is slower than rev_parse because it converts arguments one by one
722 723 724 |
# File 'lib/gitgo/git.rb', line 722 def safe_rev_parse(*args) args.collect! {|arg| rev_parse(arg).at(0) rescue nil } end |
#sandbox ⇒ Object
Creates and sets a work tree and index file so that git will have an environment it can work in. Specifically sandbox creates an empty work_tree and index_file, the sets these ENV variables:
GIT_DIR:: set to the repo path
GIT_WORK_TREE:: work_tree,
GIT_INDEX_FILE:: index_file
Once these are set, sandbox yields grit.git, the work_tree, and index_file to the block. After the block returns, the work_tree and index_file are removed. Nested calls to sandbox will reuse the previous sandbox and yield immediately to the block.
Note that no content is checked out into work_tree or index_file by this method; that must be done as needed within the block.
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 |
# File 'lib/gitgo/git.rb', line 890 def sandbox # :yields: git, work_tree, index_file if @sandbox return yield(grit.git, work_tree, index_file) end FileUtils.rm_r(work_tree) if File.exists?(work_tree) FileUtils.rm(index_file) if File.exists?(index_file) begin FileUtils.mkdir_p(work_tree) @sandbox = true with_env( 'GIT_DIR' => grit.path, 'GIT_WORK_TREE' => work_tree, 'GIT_INDEX_FILE' => index_file ) do yield(grit.git, work_tree, index_file) end ensure FileUtils.rm_r(work_tree) if File.exists?(work_tree) FileUtils.rm(index_file) if File.exists?(index_file) @sandbox = false end end |
#set(type, content) ⇒ Object
Sets an object of the specified type into the git repository and returns the object sha.
350 351 352 |
# File 'lib/gitgo/git.rb', line 350 def set(type, content) grit.git.put_raw_object(content, type.to_s) end |
#stats ⇒ Object
Returns a hash of repo statistics parsed from ‘git count-objects –verbose’.
862 863 864 865 866 867 868 869 870 871 872 873 |
# File 'lib/gitgo/git.rb', line 862 def stats sandbox do |git, work_tree, index_file| stdout, stderr = git.sh("#{Grit::Git.git_binary} count-objects --verbose") stats = YAML.load(stdout) unless stats.kind_of?(Hash) raise stderr end stats end end |
#status(full = false) ⇒ Object
Returns a hash of (path, state) pairs indicating paths that have been added or removed. States are add/rm/mod only – renames, moves, and copies are not detected.
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/gitgo/git.rb', line 544 def status(full=false) a = commit_tree.flatten b = tree.flatten diff = {} (a.keys | b.keys).collect do |key| a_entry = a.has_key?(key) ? a[key] : nil b_entry = b.has_key?(key) ? b[key] : nil change = case when a_entry && b_entry next unless a_entry != b_entry :mod when a_entry :rm when b_entry :add end diff[key] = full ? [change, a_entry || [], b_entry || []] : change end diff end |
#track(upstream_branch) ⇒ Object
Sets branch to track the specified upstream_branch. The upstream_branch must be an existing tracking branch; an error is raised if this requirement is not met (see the Tracking, Push/Pull notes above).
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/gitgo/git.rb', line 400 def track(upstream_branch) if upstream_branch.nil? # currently grit.config does not support unsetting (grit-2.0.0) grit.git.config({:unset => true}, "branch.#{branch}.remote") grit.git.config({:unset => true}, "branch.#{branch}.merge") else unless tracking_branch?(upstream_branch) raise "the upstream branch is not a tracking branch: #{upstream_branch}" end remote, remote_branch = upstream_branch.split('/', 2) grit.config["branch.#{branch}.remote"] = remote grit.config["branch.#{branch}.merge"] = "refs/heads/#{remote_branch}" end end |
#tracking_branch?(ref) ⇒ Boolean
Returns true if the specified ref is a tracking branch, ie it is the name of an existing remote ref.
443 444 445 |
# File 'lib/gitgo/git.rb', line 443 def tracking_branch?(ref) ref && grit.remotes.find {|remote| remote.name == ref } end |
#tree_grep(pattern, treeish = head) ⇒ Object
Greps for trees matching the pattern, at the specified treeish. Each matching path and tree are yielded to the block.
Instead of a pattern, a hash of grep options may be provided. The following options are allowed:
:ignore_case
:invert_match
:fixed_strings
:e
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 |
# File 'lib/gitgo/git.rb', line 800 def tree_grep(pattern, treeish=head) # :yields: path, tree = pattern.respond_to?(:merge) ? pattern.dup : {:e => pattern} .delete_if {|key, value| nil_or_empty?(value) } unless commit = grit.commit(treeish) raise "unknown commit: #{treeish}" end sandbox do |git, work_tree, index_file| postfix = .empty? ? '' : begin = git.() " | grep #{.join(' ')}" end stdout, stderr = git.sh("#{Grit::Git.git_binary} ls-tree -r --name-only #{git.e(commit.id)} #{postfix}") stdout.split("\n").each do |path| yield(path, commit.tree / path) end end self end |
#type(sha) ⇒ Object
Returns the type of the object identified by sha; the output of:
% git cat-file -t sha
324 325 326 |
# File 'lib/gitgo/git.rb', line 324 def type(sha) grit.git.cat_file({:t => true}, sha) end |
#upstream_branch ⇒ Object
Returns the upstream_branch as setup by track. Raises an error if the ‘branch.name.merge’ config doesn’t follow the pattern ‘ref/heads/branch’ (see the Tracking, Push/Pull notes above).
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/gitgo/git.rb', line 419 def upstream_branch remote = grit.config["branch.#{branch}.remote"] merge = grit.config["branch.#{branch}.merge"] # No remote, no merge, no tracking. if remote.nil? || merge.nil? return nil end unless merge =~ /^refs\/heads\/(.*)$/ raise "invalid upstream branch" end "#{remote}/#{$1}" end |