Class: PivotalIntegration::Util::Git
- Inherits:
-
Object
- Object
- PivotalIntegration::Util::Git
- Defined in:
- lib/pivotal-integration/util/git.rb
Overview
Utilities for dealing with Git
Constant Summary collapse
- KEY_REMOTE =
'remote'.freeze
- KEY_ROOT_BRANCH =
'root-branch'.freeze
- KEY_ROOT_REMOTE =
'root-remote'.freeze
- KEY_FINISH_MODE =
'finish-mode'.freeze
- RELEASE_BRANCH_NAME =
'pivotal-tracker-release'.freeze
Class Method Summary collapse
-
.add_hook(name, source, overwrite = false) ⇒ void
Adds a Git hook to the current repository.
-
.branch_name ⇒ String
Returns the name of the currently checked out branch.
-
.create_branch(name, print_messages = true) ⇒ void
Creates a branch with a given
name
. -
.create_commit(message, story) ⇒ void
Creates a commit with a given message.
- .create_pull_request(story) ⇒ Object
-
.create_release_tag(name, story) ⇒ void
Creates a tag with the given name.
- .finish_mode ⇒ Object
-
.get_config(key, scope = :inherited) ⇒ String
Returns a Git configuration value.
- .get_config_with_default(key, default = nil, scope = :inherited) ⇒ Object
-
.merge(story, no_complete, no_delete) ⇒ void
Merges the current branch to its root branch and deletes the current branch.
-
.push(*refs) ⇒ void
Push changes to the remote of the current branch.
-
.repository_root ⇒ String
Returns the root path of the current Git repository.
-
.set_config(key, value, scope = :local) ⇒ void
Sets a Git configuration value.
- .switch_branch(name) ⇒ Object
-
.trivial_merge? ⇒ void
Checks whether merging the current branch back to its root branch would be a trivial merge.
Class Method Details
.add_hook(name, source, overwrite = false) ⇒ void
This method returns an undefined value.
Adds a Git hook to the current repository
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/pivotal-integration/util/git.rb', line 35 def self.add_hook(name, source, overwrite = false) hooks_directory = File.join repository_root, '.git', 'hooks' hook = File.join hooks_directory, name if overwrite || !File.exist?(hook) print "Creating Git hook #{name}... " FileUtils.mkdir_p hooks_directory File.open(source, 'r') do |input| File.open(hook, 'w') do |output| output.write(input.read) output.chmod(0755) end end puts 'OK' end end |
.branch_name ⇒ String
Returns the name of the currently checked out branch
57 58 59 |
# File 'lib/pivotal-integration/util/git.rb', line 57 def self.branch_name PivotalIntegration::Util::Shell.exec('git branch').scan(/\* (.*)/)[0][0] end |
.create_branch(name, print_messages = true) ⇒ void
This method returns an undefined value.
Creates a branch with a given name
. First pulls the current branch to ensure that it is up to date and then creates and checks out the new branch. If specified, sets branch-specific properties that are passed in.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/pivotal-integration/util/git.rb', line 68 def self.create_branch(name, = true) root_branch = branch_name root_remote = get_config KEY_REMOTE, :branch if ; print "Pulling #{root_branch}... " end PivotalIntegration::Util::Shell.exec 'git pull --quiet --ff-only' if ; puts 'OK' end if ; print "Creating and checking out #{name}... " end PivotalIntegration::Util::Shell.exec "git checkout --quiet -B #{name}" set_config KEY_ROOT_BRANCH, root_branch, :branch set_config KEY_ROOT_REMOTE, root_remote, :branch if ; puts 'OK' end end |
.create_commit(message, story) ⇒ void
This method returns an undefined value.
Creates a commit with a given message. The commit includes all change files.
97 98 99 |
# File 'lib/pivotal-integration/util/git.rb', line 97 def self.create_commit(, story) PivotalIntegration::Util::Shell.exec "git commit --quiet --all --allow-empty --message \"#{}\n\n[##{story.id}]\"" end |
.create_pull_request(story) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/pivotal-integration/util/git.rb', line 174 def self.create_pull_request(story) case get_config_with_default('pivotal.pull-request-editor', :web).to_sym when :web # Open the PR editor in a web browser repo = get_config('remote.origin.url')[/(?<[email protected]:)[a-z0-9_-]+\/[a-z0-9_-]+/] title = CGI::escape("[##{story.id}] #{story.name}]") Launchy.open "https://github.com/#{repo}/compare/#{branch_name}?expand=1&pull_request[title]=#{title}" else print 'Checking for hub installation... ' if PivotalIntegration::Util::Shell.exec('which hub', false).empty? puts "FAIL" puts "Hub required to use this feature (brew install hub / https://github.com/github/hub)." abort else puts "OK" end puts "Creating a pull request for #{branch_name}... " system "hub pull-request" puts 'OK' end end |
.create_release_tag(name, story) ⇒ void
This method returns an undefined value.
Creates a tag with the given name. Before creating the tag, commits all outstanding changes with a commit message that reflects that these changes are for a release.
109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/pivotal-integration/util/git.rb', line 109 def self.create_release_tag(name, story) root_branch = branch_name print "Creating tag v#{name}... " create_branch RELEASE_BRANCH_NAME, false create_commit "#{name} Release", story PivotalIntegration::Util::Shell.exec "git tag v#{name}" PivotalIntegration::Util::Shell.exec "git checkout --quiet #{root_branch}" PivotalIntegration::Util::Shell.exec "git branch --quiet -D #{RELEASE_BRANCH_NAME}" puts 'OK' end |
.finish_mode ⇒ Object
148 149 150 |
# File 'lib/pivotal-integration/util/git.rb', line 148 def self.finish_mode get_config_with_default('pivotal.finish-mode', :merge).to_sym end |
.get_config(key, scope = :inherited) ⇒ String
Returns a Git configuration value. This value is read using the git config command. The scope of the value to read can be controlled with the scope
parameter.
133 134 135 136 137 138 139 140 141 |
# File 'lib/pivotal-integration/util/git.rb', line 133 def self.get_config(key, scope = :inherited) if :branch == scope PivotalIntegration::Util::Shell.exec("git config branch.#{branch_name}.#{key}", false).strip elsif :inherited == scope PivotalIntegration::Util::Shell.exec("git config #{key}", false).strip else raise "Unable to get Git configuration for scope '#{scope}'" end end |
.get_config_with_default(key, default = nil, scope = :inherited) ⇒ Object
143 144 145 146 |
# File 'lib/pivotal-integration/util/git.rb', line 143 def self.get_config_with_default(key, default = nil, scope = :inherited) value = get_config(key, scope) value.blank? ? default : value end |
.merge(story, no_complete, no_delete) ⇒ void
This method returns an undefined value.
Merges the current branch to its root branch and deletes the current branch
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/pivotal-integration/util/git.rb', line 158 def self.merge(story, no_complete, no_delete) development_branch = branch_name root_branch = get_config KEY_ROOT_BRANCH, :branch print "Merging #{development_branch} to #{root_branch}... " PivotalIntegration::Util::Shell.exec "git checkout --quiet #{root_branch}" PivotalIntegration::Util::Shell.exec "git merge --quiet --no-ff -m \"Merge #{development_branch} to #{root_branch}\n\n[#{no_complete ? '' : 'Completes '}##{story.id}]\" #{development_branch}" puts 'OK' unless no_delete print "Deleting #{development_branch}... " PivotalIntegration::Util::Shell.exec "git branch --quiet -D #{development_branch}" puts 'OK' end end |
.push(*refs) ⇒ void
This method returns an undefined value.
Push changes to the remote of the current branch
202 203 204 205 206 207 208 |
# File 'lib/pivotal-integration/util/git.rb', line 202 def self.push(*refs) remote = get_config KEY_REMOTE, :branch print "Pushing to #{remote}... " PivotalIntegration::Util::Shell.exec "git push --quiet origin #{remote} " + refs.join(' ') puts 'OK' end |
.repository_root ⇒ String
Returns the root path of the current Git repository. The root is determined by ascending the path hierarchy, starting with the current working directory (+Dir#pwd+), until a directory is found that contains a .git/
sub directory.
217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/pivotal-integration/util/git.rb', line 217 def self.repository_root repository_root = Dir.pwd until Dir.entries(repository_root).any? { |child| File.directory?(child) && (child =~ /^.git$/) } next_repository_root = File.('..', repository_root) abort('Current working directory is not in a Git repository') unless repository_root != next_repository_root repository_root = next_repository_root end repository_root end |
.set_config(key, value, scope = :local) ⇒ void
This method returns an undefined value.
Sets a Git configuration value. This value is set using the git config command. The scope of the set value can be controlled with the scope
parameter.
241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/pivotal-integration/util/git.rb', line 241 def self.set_config(key, value, scope = :local) if :branch == scope PivotalIntegration::Util::Shell.exec "git config --local branch.#{branch_name}.#{key} #{value}" elsif :global == scope PivotalIntegration::Util::Shell.exec "git config --global #{key} #{value}" elsif :local == scope PivotalIntegration::Util::Shell.exec "git config --local #{key} #{value}" else raise "Unable to set Git configuration for scope '#{scope}'" end end |
.switch_branch(name) ⇒ Object
85 86 87 |
# File 'lib/pivotal-integration/util/git.rb', line 85 def self.switch_branch(name) PivotalIntegration::Util::Shell.exec "git checkout --quiet #{name}" end |
.trivial_merge? ⇒ void
This method returns an undefined value.
Checks whether merging the current branch back to its root branch would be a trivial merge. A trivial merge is defined as one where the net change of the merge would be the same as the net change of the branch being merged. The easiest way to ensure that a merge is trivial is to rebase a development branch onto the tip of its root branch.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/pivotal-integration/util/git.rb', line 260 def self.trivial_merge? development_branch = branch_name root_branch = get_config KEY_ROOT_BRANCH, :branch print "Checking for trivial merge from #{development_branch} to #{root_branch}... " PivotalIntegration::Util::Shell.exec "git checkout --quiet #{root_branch}" PivotalIntegration::Util::Shell.exec 'git pull --quiet --ff-only' PivotalIntegration::Util::Shell.exec "git checkout --quiet #{development_branch}" root_tip = PivotalIntegration::Util::Shell.exec "git rev-parse #{root_branch}" common_ancestor = PivotalIntegration::Util::Shell.exec "git merge-base #{root_branch} #{development_branch}" if root_tip != common_ancestor abort 'FAIL' end puts 'OK' end |