Skip to content

Commit

Permalink
Merge branch 'master' into staging
Browse files Browse the repository at this point in the history
* master:
  remove whitespace
  bump version
  reorg
  Created MergeError Class & modified specs
  bump version
  bump version
  bump version
  Refactor integration command
  removed extra whitespace
  removed map, changed spec description, updated ver
  fixed merge conflicts and added red to error
  removed return true and changed to_a.map to .map
  changed version
  added ask feature for when branch doesn't exist
  fixed merge conflict error message
  changed version to 2.5.0.alpha1
  changed resume option to string and fixed tests
  added in --resume flag to integrate command
  • Loading branch information
wireframe committed Sep 23, 2014
2 parents f912b9e + ff9337c commit 159cd6a
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 27 deletions.
2 changes: 2 additions & 0 deletions lib/thegarage/gitx/cli/base_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module Cli
class BaseCommand < Thor
include Thor::Actions

class MergeError < Thor::Error; end

AGGREGATE_BRANCHES = %w( staging prototype )
RESERVED_BRANCHES = %w( HEAD master next_release ) + AGGREGATE_BRANCHES
add_runtime_options!
Expand Down
54 changes: 43 additions & 11 deletions lib/thegarage/gitx/cli/integrate_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,69 @@ module Gitx
module Cli
class IntegrateCommand < BaseCommand
desc 'integrate', 'integrate the current branch into one of the aggregate development branches (default = staging)'
def integrate(target_branch = 'staging')
branch = current_branch.name
assert_aggregate_branch!(target_branch)
method_option :resume, :type => :string, :aliases => '-r', :desc => 'resume merging of feature-branch'
def integrate(integration_branch = 'staging')
assert_aggregate_branch!(integration_branch)

branch = feature_branch_name
print_message(branch, integration_branch)

UpdateCommand.new.update

say "Integrating "
integrate_branch(branch, integration_branch) unless options[:resume]
checkout_branch branch
end

private

def print_message(branch, integration_branch)
message = options[:resume] ? 'Resuming integration of' : 'Integrating'
say "#{message} "
say "#{branch} ", :green
say "into "
say target_branch, :green
say integration_branch, :green
end

create_remote_branch(target_branch) unless remote_branch_exists?(target_branch)
refresh_branch_from_remote(target_branch)
run_cmd "git merge #{branch}"
def integrate_branch(branch, integration_branch)
fetch_remote_branch(integration_branch)
begin
run_cmd "git merge #{branch}"
rescue
fail MergeError, "Merge Conflict Occurred. Please fix merge conflict and rerun command with --resume #{branch} flag"
end
run_cmd "git push origin HEAD"
checkout_branch branch
end

private
def feature_branch_name
@feature_branch ||= begin
feature_branch = options[:resume] || current_branch.name
until local_branch_exists?(feature_branch)
feature_branch = ask("#{feature_branch} does not exist. Please select one of the available local branches: #{local_branches}")
end
feature_branch
end
end

def assert_aggregate_branch!(target_branch)
fail "Invalid aggregate branch: #{target_branch} must be one of supported aggregate branches #{AGGREGATE_BRANCHES}" unless aggregate_branch?(target_branch)
end

# nuke local branch and pull fresh version from remote repo
def refresh_branch_from_remote(target_branch)
def fetch_remote_branch(target_branch)
create_remote_branch(target_branch) unless remote_branch_exists?(target_branch)
run_cmd "git fetch origin"
run_cmd "git branch -D #{target_branch}", :allow_failure => true
checkout_branch target_branch
end

def local_branch_exists?(branch)
local_branches.include?(branch)
end

def local_branches
@local_branches ||= repo.branches.each_name(:local)
end

def remote_branch_exists?(target_branch)
repo.branches.each_name(:remote).include?("origin/#{target_branch}")
end
Expand Down
2 changes: 1 addition & 1 deletion lib/thegarage/gitx/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Thegarage
module Gitx
VERSION = '2.4.2'
VERSION = '2.5.0'
end
end
14 changes: 4 additions & 10 deletions spec/support/stub_execution.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
# protip to prevent execution of shell commands during testsuite
# see http://stackoverflow.com/questions/1628586/mock-system-call-in-ruby
module ShellExecutionStub
def included(base)
base.class_eval do
alias_method :execute_without_stub, :`
alias_method :`, :execute_with_stub
end
end

module Kernel
def execute_with_stub(cmd)
if cmd.include?('git')
puts "WARNING: stubbing command execution within tests of command: #{cmd}", :red
else
execute_without_stub(cmd)
end
end
end

Kernel.send(:include, ShellExecutionStub)
alias_method :execute_without_stub, :`
alias_method :`, :execute_with_stub
end
73 changes: 68 additions & 5 deletions spec/thegarage/gitx/cli/integrate_command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@
let(:cli) { Thegarage::Gitx::Cli::IntegrateCommand.new(args, options, config) }
let(:current_branch) { double('fake branch', name: 'feature-branch', head?: true) }
let(:repo) { cli.send(:repo) }
let(:remote_branch_names) { [] }
let(:remote_branch_names) { ['origin/staging', 'origin/prototype'] }
let(:local_branch_names) { ['feature-branch'] }

before do
allow(cli).to receive(:current_branch).and_return(current_branch)
allow(repo).to receive(:branches).and_return(double(each_name: remote_branch_names))
branches = double('fake branches')
allow(branches).to receive(:each_name).with(:local).and_return(local_branch_names)
allow(branches).to receive(:each_name).with(:remote).and_return(remote_branch_names)
allow(repo).to receive(:branches).and_return(branches)
end

describe '#integrate' do
let(:fake_update_command) { double('fake update command') }
before do
allow(Thegarage::Gitx::Cli::UpdateCommand).to receive(:new).and_return(fake_update_command)
end
context 'when target branch is ommitted and remote branch exists' do
context 'when integration branch is ommitted and remote branch exists' do
let(:remote_branch_names) { ['origin/staging'] }
before do
expect(fake_update_command).to receive(:update)
Expand Down Expand Up @@ -64,7 +68,7 @@
should meet_expectations
end
end
context 'when target branch == prototype and remote branch exists' do
context 'when integration branch == prototype and remote branch exists' do
let(:remote_branch_names) { ['origin/prototype'] }
before do
expect(fake_update_command).to receive(:update)
Expand All @@ -82,10 +86,69 @@
should meet_expectations
end
end
context 'when target branch is not an aggregate branch' do
context 'when integration branch is not an aggregate branch' do
it 'raises an error' do
expect { cli.integrate('some-other-branch') }.to raise_error(/Invalid aggregate branch: some-other-branch must be one of supported aggregate branches/)
end
end
context 'when merge conflicts occur' do
let(:remote_branch_names) { ['origin/staging'] }
before do
expect(fake_update_command).to receive(:update)

expect(cli).to receive(:run_cmd).with("git fetch origin").ordered
expect(cli).to receive(:run_cmd).with("git branch -D staging", allow_failure: true).ordered
expect(cli).to receive(:run_cmd).with("git checkout staging").ordered
expect(cli).to receive(:run_cmd).with("git merge feature-branch").and_raise('git merge feature-branch failed').ordered

expect { cli.integrate }.to raise_error(/Merge Conflict Occurred. Please fix merge conflict and rerun command with --resume feature-branch flag/)
end
it 'raises a helpful error' do
should meet_expectations
end
end
context 'with --resume "feature-branch" flag when feature-branch exists' do
let(:options) do
{
resume: 'feature-branch'
}
end
let(:repo) { cli.send(:repo) }
before do
expect(fake_update_command).to receive(:update)

expect(cli).not_to receive(:run_cmd).with("git branch -D staging")
expect(cli).not_to receive(:run_cmd).with("git push origin HEAD")
expect(cli).to receive(:run_cmd).with("git checkout feature-branch")

cli.integrate
end
it 'does not delete local staging branch' do
should meet_expectations
end
end
context 'with --resume "feature-branch" flag when feature-branch does not exist' do
let(:options) do
{
resume: 'feature-branch'
}
end
let(:repo) { cli.send(:repo) }
let(:branches) { double(each_name: ['my-feature-branch'])}
let(:local_branch_names) { ['another-feature-branch'] }
before do
expect(fake_update_command).to receive(:update)
expect(cli).to receive(:ask).and_return('another-feature-branch')

expect(cli).not_to receive(:run_cmd).with("git branch -D staging")
expect(cli).not_to receive(:run_cmd).with("git push origin HEAD")
expect(cli).to receive(:run_cmd).with("git checkout another-feature-branch").ordered

cli.integrate
end
it 'asks user for feature-branch name' do
should meet_expectations
end
end
end
end

0 comments on commit 159cd6a

Please sign in to comment.