git_reflow 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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