git_reflow 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Appraisals +2 -2
  4. data/CHANGELOG.md +75 -0
  5. data/Gemfile.lock +34 -35
  6. data/README.rdoc +187 -60
  7. data/circle.yml +9 -9
  8. data/git_reflow.gemspec +8 -8
  9. data/lib/git_reflow/commands/deliver.rb +1 -9
  10. data/lib/git_reflow/commands/refresh.rb +23 -0
  11. data/lib/git_reflow/commands/review.rb +7 -7
  12. data/lib/git_reflow/commands/setup.rb +7 -3
  13. data/lib/git_reflow/commands/start.rb +13 -5
  14. data/lib/git_reflow/git_helpers.rb +22 -23
  15. data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +4 -4
  16. data/lib/git_reflow/git_server/bit_bucket.rb +7 -7
  17. data/lib/git_reflow/git_server/git_hub/pull_request.rb +75 -2
  18. data/lib/git_reflow/git_server/git_hub.rb +17 -8
  19. data/lib/git_reflow/git_server/pull_request.rb +73 -5
  20. data/lib/git_reflow/git_server.rb +3 -3
  21. data/lib/git_reflow/merge_error.rb +9 -0
  22. data/lib/git_reflow/sandbox.rb +4 -16
  23. data/lib/git_reflow/version.rb +1 -1
  24. data/lib/git_reflow.rb +32 -75
  25. data/spec/git_reflow_spec.rb +157 -141
  26. data/spec/lgtm_git_reflow_spec.rb +165 -139
  27. data/spec/lib/git_reflow/git_helpers_spec.rb +19 -63
  28. data/spec/lib/git_reflow/git_server_spec.rb +24 -24
  29. data/spec/lib/git_server/bit_bucket_spec.rb +12 -12
  30. data/spec/lib/git_server/git_hub/pull_request_spec.rb +7 -5
  31. data/spec/lib/git_server/git_hub_spec.rb +34 -34
  32. data/spec/lib/git_server/pull_request_spec.rb +207 -16
  33. data/spec/support/command_line_helpers.rb +14 -9
  34. data/spec/support/github_helpers.rb +21 -21
  35. data/spec/support/rspec_stub_helpers.rb +2 -2
  36. metadata +18 -16
data/git_reflow.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ensure we require the local version and not one we might have installed already
2
2
  require File.join([File.dirname(__FILE__),'lib','git_reflow/version.rb'])
3
- spec = Gem::Specification.new do |s|
3
+ Gem::Specification.new do |s|
4
4
  s.name = 'git_reflow'
5
5
  s.version = GitReflow::VERSION
6
6
  s.license = 'MIT'
@@ -19,21 +19,21 @@ spec = Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
  s.rdoc_options << '--title' << 'git_reflow' << '--main' << 'README.rdoc' << '-ri'
21
21
 
22
- s.add_development_dependency('appraisal', '1.0.3')
23
- s.add_development_dependency('bundler', "~> 1.11")
22
+ s.add_development_dependency('appraisal', '2.1.0')
23
+ s.add_development_dependency('bundler', "~> 1.12")
24
24
  s.add_development_dependency('chronic')
25
25
  s.add_development_dependency('pry-byebug')
26
- s.add_development_dependency('rake', "~> 10.0")
26
+ s.add_development_dependency('rake', "~> 11.0")
27
27
  s.add_development_dependency('rdoc')
28
- s.add_development_dependency('rspec', "~> 3.0")
28
+ s.add_development_dependency('rspec', "~> 3.4.0")
29
29
  s.add_development_dependency('webmock')
30
- s.add_development_dependency('wwtd', '0.7.0')
30
+ s.add_development_dependency('wwtd', '1.3.0')
31
31
 
32
32
  s.add_dependency('colorize', '>= 0.7.0')
33
- s.add_dependency('gli', '2.13.2')
33
+ s.add_dependency('gli', '2.14.0')
34
34
  s.add_dependency('highline')
35
35
  s.add_dependency('httpclient')
36
- s.add_dependency('github_api', '0.12.4')
36
+ s.add_dependency('github_api', '0.14.0')
37
37
  s.add_dependency('reenhanced_bitbucket_api', '0.3.2')
38
38
 
39
39
  s.post_install_message = "You need to setup your GitHub OAuth token\nPlease run 'git-reflow setup'"
@@ -3,17 +3,9 @@ long_desc 'merge your feature branch down to your base branch, and cleanup your
3
3
 
4
4
  command :deliver do |c|
5
5
  c.desc 'merge your feature branch down to your base branch, and cleanup your feature branch'
6
- c.arg_name 'base_branch - the branch you want to merge into'
7
6
  c.switch [:f, :'skip-lgtm'], desc: 'skip the lgtm checks and deliver your feature branch'
8
7
  c.action do |global_options,options,args|
9
- deliver_options = {'base' => nil, 'head' => nil, 'skip_lgtm' => options[:'skip-lgtm']}
10
- case args.length
11
- when 2
12
- deliver_options['base'] = args[0]
13
- deliver_options['head'] = args[1]
14
- when 1
15
- deliver_options['base'] = args[0]
16
- end
8
+ deliver_options = {:skip_lgtm => options[:'skip-lgtm']}
17
9
  GitReflow.deliver deliver_options
18
10
  end
19
11
  end
@@ -0,0 +1,23 @@
1
+ desc 'Updates and synchronizes your base branch and feature branch.'
2
+ long_desc <<LONGTIME
3
+ Performs the following:\n
4
+ \t$ git checkout <base_branch>\n
5
+ \t$ git pull <remote_location> <base_branch>\n
6
+ \t$ git checkout <current_branch>\n
7
+ \t$ git pull origin <current_branch>\n
8
+ \t$ git merge <base_branch>\n
9
+ LONGTIME
10
+ arg_name '[remote_location] - remote repository name to fetch updates from (origin by default), [base_branch] - branch that you want to merge with (master by default)'
11
+ command :refresh do |c|
12
+ c.desc 'updates base_branch based on remote and merges the base with your feature_branch'
13
+ c.flag [:r,:remote], default_value: 'origin'
14
+ c.flag [:b,:base], default_value: 'master'
15
+ c.action do |global_options, options, args|
16
+ refresh_options = {
17
+ :remote => options[:remote],
18
+ :base => options[:base]
19
+ }
20
+
21
+ GitReflow.update_feature_branch(refresh_options)
22
+ end
23
+ end
@@ -3,17 +3,17 @@ arg_name 'Describe arguments to review here'
3
3
  command :review do |c|
4
4
  c.desc 'push your latest feature branch changes to your remote repo and create a pull request against the destination branch'
5
5
  c.arg_name '[destination_branch] - the branch you want to merge your feature branch into'
6
- c.flag [:t, :title], default_value: 'last commit message'
7
- c.flag [:m, :message], default_value: 'title'
6
+ c.flag [:t, :title]
7
+ c.flag [:m, :message]
8
8
  c.action do |global_options,options,args|
9
- if global_options[:title] || global_options[:message]
9
+ if options[:title] || options[:message]
10
10
  review_options = {
11
- 'base' => args[0],
12
- 'title' => global_options[:title],
13
- 'body' => global_options[:message]
11
+ :base => args[0],
12
+ :title => options[:title],
13
+ :body => options[:message]
14
14
  }
15
15
  else
16
- review_options = { 'base' => args[0] }
16
+ review_options = { :base => args[0] }
17
17
  end
18
18
 
19
19
  GitReflow.review(review_options)
@@ -22,8 +22,12 @@ command :setup do |c|
22
22
  menu.choice('BitBucket (team-owned repos only)') { GitReflow::GitServer.connect reflow_options.merge({ provider: 'BitBucket', silent: false }) }
23
23
  end
24
24
 
25
- GitReflow::Config.add "constants.minimumApprovals", ask("Set the minimum number of approvals (leaving blank will require approval from all commenters): "), local: reflow_options[:project_only]
26
- GitReflow::Config.add "constants.approvalRegex", GitReflow::GitServer::PullRequest::DEFAULT_APPROVAL_REGEX, local: reflow_options[:project_only]
27
-
25
+ GitReflow::Config.set "constants.minimumApprovals", ask("Set the minimum number of approvals (leaving blank will require approval from all commenters): "), local: reflow_options[:project_only]
26
+ GitReflow::Config.set "constants.approvalRegex", GitReflow::GitServer::PullRequest::DEFAULT_APPROVAL_REGEX.to_s, local: reflow_options[:project_only]
27
+
28
+ if GitReflow::Config.get('core.editor').length <= 0
29
+ GitReflow::Config.set('core.editor', GitReflow::DEFAULT_EDITOR, local: reflow_options[:project_only])
30
+ GitReflow.say "Updated git's editor (via git config key 'core.editor') to: #{GitReflow::DEFAULT_EDITOR}.", :notice
31
+ end
28
32
  end
29
33
  end
@@ -1,19 +1,27 @@
1
+
1
2
  desc 'Start will create a new feature branch and setup remote tracking'
2
3
  long_desc <<LONGTIME
3
4
  Performs the following:\n
4
- \t$ git pull origin <current_branch>\n
5
- \t$ git push origin <current_branch>:refs/heads/[new_feature_branch]\n
5
+ \t$ git checkout <base_branch>\n
6
+ \t$ git pull origin <base_branch>\n
7
+ \t$ git push origin <base_branch>:refs/heads/[new_feature_branch]\n
6
8
  \t$ git checkout --track -b [new_feature_branch] origin/[new_feature_branch]\n
7
9
  LONGTIME
8
10
  arg_name '[new-feature-branch-name] - name of the new feature branch'
9
11
  command :start do |c|
12
+ c.flag [:b,:base], default_value: 'master'
10
13
  c.action do |global_options, options, args|
11
14
  if args.empty?
12
15
  raise "usage: git-reflow start [new-branch-name]"
13
16
  else
14
- `git pull origin #{GitReflow.current_branch}`
15
- `git push origin #{GitReflow.current_branch}:refs/heads/#{args[0]}`
16
- `git checkout --track -b #{args[0]} origin/#{args[0]}`
17
+ # base_branch is the branch that you want to base your feature branch off of
18
+ # This command allows you to 'git reflow start' off your base branch
19
+ base_branch = options[:base]
20
+
21
+ GitReflow.run_command_with_label "git checkout #{base_branch}"
22
+ GitReflow.run_command_with_label "git pull origin #{base_branch}"
23
+ GitReflow.run_command_with_label "git push origin #{GitReflow.current_branch}:refs/heads/#{args[0]}"
24
+ GitReflow.run_command_with_label "git checkout --track -b #{args[0]} origin/#{args[0]}"
17
25
  end
18
26
  end
19
27
  end
@@ -9,6 +9,10 @@ module GitReflow
9
9
  @git_root_dir ||= run('git rev-parse --show-toplevel', loud: false).strip
10
10
  end
11
11
 
12
+ def git_editor_command
13
+ @git_editor_comand ||= GitReflow::Config.get('core.editor')
14
+ end
15
+
12
16
  def remote_user
13
17
  return "" unless "#{GitReflow::Config.get('remote.origin.url')}".length > 0
14
18
  extract_remote_user_and_repo_from_remote_url(GitReflow::Config.get('remote.origin.url'))[:user]
@@ -27,43 +31,38 @@ module GitReflow
27
31
  run('git log --pretty=format:"%s" --no-merges -n 1', loud: false).strip
28
32
  end
29
33
 
30
- def push_current_branch
31
- run_command_with_label "git push origin #{current_branch}"
34
+ def push_current_branch(options = {})
35
+ remote = options[:remote] || "origin"
36
+ run_command_with_label "git push #{remote} #{current_branch}"
32
37
  end
33
38
 
34
- def update_current_branch
35
- run_command_with_label "git pull origin #{current_branch}"
36
- push_current_branch
39
+ def update_current_branch(options = {})
40
+ remote = options[:remote] || "origin"
41
+ run_command_with_label "git pull #{remote} #{current_branch}"
42
+ push_current_branch(options)
37
43
  end
38
44
 
39
45
  def fetch_destination(destination_branch)
40
46
  run_command_with_label "git fetch origin #{destination_branch}"
41
47
  end
42
48
 
43
- def update_destination(destination_branch)
49
+ def update_destination(destination_branch, options = {})
44
50
  origin_branch = current_branch
51
+ remote = options[:remote] || 'origin'
45
52
  run_command_with_label "git checkout #{destination_branch}"
46
- run_command_with_label "git pull origin #{destination_branch}"
53
+ run_command_with_label "git pull #{remote} #{destination_branch}"
47
54
  run_command_with_label "git checkout #{origin_branch}"
48
55
  end
49
56
 
50
- def merge_feature_branch(feature_branch_name, options = {})
51
- options[:destination_branch] ||= 'master'
52
-
53
- message = "#{options[:message]}"
54
-
55
- if "#{options[:pull_request_number]}".length > 0
56
- message << "\nCloses ##{options[:pull_request_number]}\n"
57
- end
58
-
59
- if lgtm_authors = Array(options[:lgtm_authors]) and lgtm_authors.any?
60
- message << "\nLGTM given by: @#{lgtm_authors.join(', @')}\n"
61
- end
57
+ def update_feature_branch(options = {})
58
+ base_branch = options[:base]
59
+ remote = options[:remote]
60
+ update_destination(base_branch, options)
62
61
 
63
- run_command_with_label "git checkout #{options[:destination_branch]}"
64
- run_command_with_label "git merge --squash #{feature_branch_name}"
65
-
66
- append_to_squashed_commit_message(message) if message.length > 0
62
+ # update feature branch in case there are multiple authors and remote changes
63
+ run_command_with_label "git pull origin #{current_branch}"
64
+ # rebase on base branch
65
+ run_command_with_label "git merge #{base_branch}"
67
66
  end
68
67
 
69
68
  def append_to_squashed_commit_message(message = '')
@@ -6,10 +6,10 @@ module GitReflow
6
6
  class PullRequest < GitReflow::GitServer::PullRequest
7
7
  def initialize(attributes)
8
8
  self.number = attributes.id
9
- self.description = attributes.description
9
+ self.description = attributes.body || attributes.description
10
10
  self.html_url = "#{attributes.source.repository.links.html.href}/pull-request/#{self.number}"
11
- self.feature_branch_name = attributes.source.branch.name
12
- self.base_branch_name = attributes.destination.branch.name
11
+ self.feature_branch_name = attributes.source.branch.name[/[^:]+$/]
12
+ self.base_branch_name = attributes.destination.branch.name[/[^:]+$/]
13
13
  self.build_status = nil
14
14
  self.source_object = attributes
15
15
  end
@@ -19,7 +19,7 @@ module GitReflow
19
19
  GitReflow.git_server.class.remote_user,
20
20
  GitReflow.git_server.class.remote_repo_name,
21
21
  title: options[:title],
22
- body: options[:body],
22
+ description: options[:body] || options[:description],
23
23
  source: {
24
24
  branch: { name: GitReflow.git_server.class.current_branch },
25
25
  repository: { full_name: "#{GitReflow.git_server.class.remote_user}/#{GitReflow.git_server.class.remote_repo_name}" }
@@ -56,21 +56,21 @@ module GitReflow
56
56
  begin
57
57
  if connection and self.class.api_key_setup?
58
58
  unless options[:silent]
59
- puts "\nYour BitBucket account was already setup with:"
60
- puts "\tUser Name: #{self.class.user}"
59
+ GitReflow.say "\nYour BitBucket account was already setup with:"
60
+ GitReflow.say "\tUser Name: #{self.class.user}"
61
61
  end
62
62
  else
63
63
  self.class.user = options[:user] || ask("Please enter your BitBucket username: ")
64
- puts "\nIn order to connect your BitBucket account,"
65
- puts "you'll need to generate an API key for your team"
66
- puts "Visit #{self.class.site_url}/account/user/#{self.class.remote_user}/api-key/, to generate it\n"
64
+ GitReflow.say "\nIn order to connect your BitBucket account,"
65
+ GitReflow.say "you'll need to generate an API key for your team"
66
+ GitReflow.say "Visit #{self.class.site_url}/account/user/#{self.class.remote_user}/api-key/, to generate it\n"
67
67
  self.class.api_key = ask("Please enter your team's API key: ")
68
68
  connection.repos.all(self.class.remote_user).count
69
- self.class.say "Connected to BitBucket\!", :success
69
+ GitReflow.say "Connected to BitBucket\!", :success
70
70
  end
71
71
  rescue ::BitBucket::Error::Unauthorized => e
72
72
  GitReflow::Config.unset('bitbucket.api-key', local: self.class.project_only?)
73
- self.class.say "Invalid API key for team #{self.class.remote_user}.", :error
73
+ GitReflow.say "Invalid API key for team #{self.class.remote_user}.", :error
74
74
  end
75
75
  end
76
76
 
@@ -8,8 +8,8 @@ module GitReflow
8
8
  self.number = attributes.number
9
9
  self.description = attributes[:body]
10
10
  self.html_url = attributes.html_url
11
- self.feature_branch_name = attributes.head.label
12
- self.base_branch_name = attributes.base.label
11
+ self.feature_branch_name = attributes.head.label[/[^:]+$/]
12
+ self.base_branch_name = attributes.base.label[/[^:]+$/]
13
13
  self.source_object = attributes
14
14
  self.build_status = build.state
15
15
  end
@@ -78,6 +78,79 @@ module GitReflow
78
78
  end
79
79
  end
80
80
 
81
+ def merge!(options = {})
82
+
83
+ # fallback to default merge process if user "forces" merge
84
+ if(options[:skip_lgtm])
85
+ super options
86
+ else
87
+ if deliver?
88
+ GitReflow.say "Merging pull request ##{self.number}: '#{self.title}', from '#{self.feature_branch_name}' into '#{self.base_branch_name}'", :notice
89
+
90
+ unless options[:title] || options[:message]
91
+ # prompts user for commit_title and commit_message
92
+ squash_merge_message_file = "#{GitReflow.git_root_dir}/.git/SQUASH_MSG"
93
+
94
+ File.open(squash_merge_message_file, 'w') do |file|
95
+ file.write("#{self.title}\n#{self.commit_message_for_merge}\n")
96
+ end
97
+
98
+ GitReflow.run("#{GitReflow.git_editor_command} #{squash_merge_message_file}", with_system: true)
99
+ merge_message = File.read(squash_merge_message_file).split(/[\r\n]|\r\n/).map(&:strip)
100
+
101
+ title = merge_message.shift
102
+
103
+ File.delete(squash_merge_message_file)
104
+
105
+ unless merge_message.empty?
106
+ merge_message.shift if merge_message.first.empty?
107
+ end
108
+
109
+ options[:title] = title
110
+ options[:body] = "#{merge_message.join("\n")}\n"
111
+
112
+ GitReflow.say "\nReview your merge commit message:\n"
113
+ GitReflow.say "--------\n"
114
+ GitReflow.say "Title:\n#{options[:title]}\n\n"
115
+ GitReflow.say "Body:\n#{options[:body]}\n"
116
+ GitReflow.say "--------\n"
117
+ end
118
+
119
+ merge_response = GitReflow::GitServer::GitHub.connection.pull_requests.merge(
120
+ "#{GitReflow.git_server.class.remote_user}",
121
+ "#{GitReflow.git_server.class.remote_repo_name}",
122
+ "#{self.number}",
123
+ {
124
+ "commit_title" => "#{options[:title]}",
125
+ "commit_message" => "#{options[:body]}",
126
+ "sha" => "#{self.head.sha}",
127
+ "squash" => true
128
+ }
129
+ )
130
+
131
+ if merge_response.success?
132
+ GitReflow.run_command_with_label "git checkout #{self.base_branch_name}"
133
+ # Pulls merged changes from remote base_branch
134
+ GitReflow.run_command_with_label "git pull origin #{self.base_branch_name}"
135
+ GitReflow.say "Pull request ##{self.number} successfully merged.", :success
136
+
137
+ if cleanup_feature_branch?
138
+ GitReflow.run_command_with_label "git push origin :#{self.feature_branch_name}"
139
+ GitReflow.run_command_with_label "git branch -D #{self.feature_branch_name}"
140
+ GitReflow.say "Nice job buddy."
141
+ else
142
+ cleanup_failure_message
143
+ end
144
+ else
145
+ GitReflow.say merge_response.to_s, :deliver_halted
146
+ GitReflow.say "There were problems commiting your feature... please check the errors above and try again.", :error
147
+ end
148
+ else
149
+ GitReflow.say "Merge aborted", :deliver_halted
150
+ end
151
+ end
152
+ end
153
+
81
154
  def build
82
155
  github_build_status = GitReflow.git_server.get_build_status(self.head.sha)
83
156
  build_status_object = Struct.new(:state, :description, :url)
@@ -86,9 +86,9 @@ module GitReflow
86
86
  def authenticate(options = {silent: false})
87
87
  if connection and self.class.oauth_token.length > 0
88
88
  unless options[:silent]
89
- puts "Your GitHub account was already setup with: "
90
- puts "\tUser Name: #{self.class.user}"
91
- puts "\tEndpoint: #{self.class.api_endpoint}"
89
+ GitReflow.say "Your GitHub account was already setup with: "
90
+ GitReflow.say "\tUser Name: #{self.class.user}"
91
+ GitReflow.say "\tEndpoint: #{self.class.api_endpoint}"
92
92
  end
93
93
  else
94
94
  begin
@@ -107,6 +107,9 @@ module GitReflow
107
107
  previous_authorizations = @connection.oauth.all.select {|auth| auth.note == "git-reflow (#{run('hostname', loud: false).strip})" }
108
108
  if previous_authorizations.any?
109
109
  authorization = previous_authorizations.last
110
+ GitReflow.say "You have previously setup git-reflow on this machine, but we can no longer find the stored token.", :error
111
+ GitReflow.say "Please visit https://github.com/settings/tokens and delete the token for: git-reflow (#{run('hostname', loud: false).strip})", :notice
112
+ raise "Setup could not be completed."
110
113
  else
111
114
  authorization = @connection.oauth.create scopes: ['repo'], note: "git-reflow (#{run('hostname', loud: false).strip})"
112
115
  end
@@ -115,15 +118,21 @@ module GitReflow
115
118
 
116
119
  rescue ::Github::Error::Unauthorized => e
117
120
  if e.inspect.to_s.include?('two-factor')
118
- two_factor_code = ask("Please enter your two-factor authentication code: ")
119
- self.authenticate options.merge({user: gh_user, password: gh_password, two_factor_auth_code: two_factor_code})
121
+ begin
122
+ # dummy request to trigger a 2FA SMS since a HTTP GET won't do it
123
+ @connection.oauth.create scopes: ['repo'], note: "thank Github for not making this straightforward"
124
+ rescue ::Github::Error::Unauthorized
125
+ ensure
126
+ two_factor_code = ask("Please enter your two-factor authentication code: ")
127
+ self.authenticate options.merge({user: gh_user, password: gh_password, two_factor_auth_code: two_factor_code})
128
+ end
120
129
  else
121
- puts "\nGithub Authentication Error: #{e.inspect}"
130
+ GitReflow.say "Github Authentication Error: #{e.inspect}", :error
122
131
  end
123
132
  rescue StandardError => e
124
- puts "\nInvalid username or password: #{e.inspect}"
133
+ raise "We were unable to authenticate with Github."
125
134
  else
126
- puts "\nYour GitHub account was successfully setup!"
135
+ GitReflow.say "Your GitHub account was successfully setup!", :success
127
136
  end
128
137
  end
129
138
 
@@ -99,12 +99,12 @@ module GitReflow
99
99
  "url" => self.html_url
100
100
  }
101
101
 
102
- notices = ""
102
+ notices = []
103
103
  reviewed_by = []
104
104
 
105
105
  # check for CI build status
106
106
  if self.build_status
107
- notices << "[notice] Your build status is not successful: #{self.build.url}.\n" unless self.build.state == "success"
107
+ notices << "Your build status is not successful: #{self.build.url}.\n" unless self.build.state == "success"
108
108
  summary_data.merge!( "Build status" => GitReflow.git_server.colorized_build_description(self.build.state, self.build.description) )
109
109
  end
110
110
 
@@ -117,9 +117,9 @@ module GitReflow
117
117
  reviewed_by.map! { |author| approvals.include?(author.uncolorize) ? author.colorize(:green) : author }
118
118
  end
119
119
 
120
- notices << "[notice] You still need a LGTM from: #{reviewers_pending_response.join(', ')}\n" if reviewers_pending_response.any?
120
+ notices << "You still need a LGTM from: #{reviewers_pending_response.join(', ')}\n" if reviewers_pending_response.any?
121
121
  else
122
- notices << "[notice] No one has reviewed your pull request.\n"
122
+ notices << "No one has reviewed your pull request.\n"
123
123
  end
124
124
 
125
125
  summary_data['reviewed by'] = reviewed_by.join(', ')
@@ -130,7 +130,9 @@ module GitReflow
130
130
  printf string_format, "#{name}:", summary_data[name]
131
131
  end
132
132
 
133
- puts "\n#{notices}" unless notices.empty?
133
+ notices.each do |notice|
134
+ GitReflow.say notice, :notice
135
+ end
134
136
  end
135
137
 
136
138
  def method_missing(method_sym, *arguments, &block)
@@ -140,6 +142,72 @@ module GitReflow
140
142
  super
141
143
  end
142
144
  end
145
+
146
+ def commit_message_for_merge
147
+ message = ""
148
+
149
+ if "#{self.description}".length > 0
150
+ message << "#{self.description}"
151
+ else
152
+ message << "#{GitReflow.get_first_commit_message}"
153
+ end
154
+
155
+ message << "\nMerges ##{self.number}\n"
156
+
157
+ if lgtm_authors = Array(self.approvals) and lgtm_authors.any?
158
+ message << "\nLGTM given by: @#{lgtm_authors.join(', @')}\n"
159
+ end
160
+
161
+ "#{message}\n"
162
+ end
163
+
164
+ def cleanup_feature_branch?
165
+ GitReflow::Config.get('reflow.always-cleanup') == "true" || (ask "Would you like to push this branch to your remote repo and cleanup your feature branch? ") =~ /^y/i
166
+ end
167
+
168
+ def deliver?
169
+ GitReflow::Config.get('reflow.always-deliver') == "true" || (ask "This is the current status of your Pull Request. Are you sure you want to deliver? ") =~ /^y/i
170
+ end
171
+
172
+ def cleanup_failure_message
173
+ GitReflow.say "Cleanup halted. Local changes were not pushed to remote repo.", :deliver_halted
174
+ GitReflow.say "To reset and go back to your branch run \`git reset --hard origin/#{self.base_branch_name} && git checkout #{self.feature_branch_name}\`"
175
+ end
176
+
177
+ def merge!(options = {})
178
+ if deliver?
179
+
180
+ GitReflow.say "Merging pull request ##{self.number}: '#{self.title}', from '#{self.feature_branch_name}' into '#{self.base_branch_name}'", :notice
181
+
182
+ GitReflow.update_current_branch
183
+ GitReflow.fetch_destination(self.base_branch_name)
184
+
185
+ message = commit_message_for_merge
186
+
187
+ GitReflow.run_command_with_label "git checkout #{self.base_branch_name}"
188
+ GitReflow.run_command_with_label "git pull origin #{self.base_branch_name}"
189
+ GitReflow.run_command_with_label "git merge --squash #{self.feature_branch_name}"
190
+
191
+ GitReflow.append_to_squashed_commit_message(message) if message.length > 0
192
+
193
+ if GitReflow.run_command_with_label 'git commit', with_system: true
194
+ GitReflow.say "Pull request ##{self.number} successfully merged.", :success
195
+
196
+ if cleanup_feature_branch?
197
+ GitReflow.run_command_with_label "git push origin #{self.base_branch_name}"
198
+ GitReflow.run_command_with_label "git push origin :#{self.feature_branch_name}"
199
+ GitReflow.run_command_with_label "git branch -D #{self.feature_branch_name}"
200
+ GitReflow.say "Nice job buddy."
201
+ else
202
+ cleanup_failure_message
203
+ end
204
+ else
205
+ GitReflow.say "There were problems commiting your feature... please check the errors above and try again.", :error
206
+ end
207
+ else
208
+ GitReflow.say "Merge aborted", :deliver_halted
209
+ end
210
+ end
143
211
  end
144
212
  end
145
213
  end
@@ -17,7 +17,7 @@ module GitReflow
17
17
  provider.authenticate(options.keep_if {|key, value| key == :silent })
18
18
  provider
19
19
  rescue ConnectionError => e
20
- puts "Error connecting to #{provider_name}: #{e.message}"
20
+ GitReflow.say "Error connecting to #{provider_name}: #{e.message}", :error
21
21
  end
22
22
  end
23
23
 
@@ -32,11 +32,11 @@ module GitReflow
32
32
  begin
33
33
  provider_class_for(provider)
34
34
  rescue ConnectionError => e
35
- puts e.message
35
+ GitReflow.say e.message, :error
36
36
  nil
37
37
  end
38
38
  else
39
- puts "[notice] Reflow hasn't been setup yet. Run 'git reflow setup' to continue"
39
+ GitReflow.say "Reflow hasn't been setup yet. Run 'git reflow setup' to continue", :notice
40
40
  nil
41
41
  end
42
42
  end
@@ -0,0 +1,9 @@
1
+ module GitReflow
2
+ module GitServer
3
+ class MergeError < StandardError
4
+ def initialize(msg="Merge failed")
5
+ super(msg)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -7,7 +7,8 @@ module GitReflow
7
7
  error: :red,
8
8
  deliver_halted: :red,
9
9
  review_halted: :red,
10
- success: :green
10
+ success: :green,
11
+ plain: :white
11
12
  }
12
13
 
13
14
  def run(command, options = {})
@@ -32,25 +33,12 @@ module GitReflow
32
33
 
33
34
  def say(message, label_type = :plain)
34
35
  if COLOR_FOR_LABEL[label_type]
35
- puts "[#{ label_type.to_s.gsub('_', ' ').colorize(COLOR_FOR_LABEL[label_type]) }] #{message}"
36
+ label = (label_type.to_s == "plain") ? "" : "[#{ label_type.to_s.gsub('_', ' ').colorize(COLOR_FOR_LABEL[label_type]) }] "
37
+ puts "#{label}#{message}"
36
38
  else
37
39
  puts message
38
40
  end
39
41
  end
40
42
 
41
- # WARNING: this currently only supports OS X and UBUNTU
42
- def ask_to_open_in_browser(url)
43
- if OS.unix?
44
- open_in_browser = ask "Would you like to open it in your browser? "
45
- if open_in_browser =~ /^y/i
46
- if OS.mac?
47
- run "open #{url}"
48
- else
49
- run "xdg-open #{url}"
50
- end
51
- end
52
- end
53
- end
54
-
55
43
  end
56
44
  end
@@ -1,3 +1,3 @@
1
1
  module GitReflow
2
- VERSION = "0.7.5"
2
+ VERSION = "0.8.0"
3
3
  end