git_helper 1.2.0 → 2.0.2

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/Gemfile.lock +17 -13
  3. data/README.md +27 -33
  4. data/Rakefile +1 -37
  5. data/bin/git-helper +10 -28
  6. data/lib/git_helper.rb +3 -5
  7. data/lib/git_helper/change_remote.rb +53 -40
  8. data/lib/git_helper/checkout_default.rb +1 -1
  9. data/lib/git_helper/clean_branches.rb +1 -4
  10. data/lib/git_helper/code_request.rb +95 -0
  11. data/lib/git_helper/empty_commit.rb +1 -1
  12. data/lib/git_helper/forget_local_commits.rb +7 -0
  13. data/lib/git_helper/gitlab_client.rb +0 -1
  14. data/lib/git_helper/highline_cli.rb +72 -6
  15. data/lib/git_helper/local_code.rb +124 -0
  16. data/lib/git_helper/merge_request.rb +57 -126
  17. data/lib/git_helper/new_branch.rb +2 -11
  18. data/lib/git_helper/octokit_client.rb +0 -1
  19. data/lib/git_helper/pull_request.rb +45 -110
  20. data/lib/git_helper/version.rb +1 -1
  21. data/spec/git_helper/change_remote_spec.rb +173 -0
  22. data/spec/git_helper/checkout_default_spec.rb +19 -0
  23. data/spec/git_helper/clean_branches_spec.rb +19 -0
  24. data/spec/git_helper/code_request_spec.rb +259 -0
  25. data/spec/git_helper/empty_commit_spec.rb +19 -0
  26. data/spec/git_helper/forget_local_commits_spec.rb +19 -0
  27. data/spec/git_helper/git_config_reader_spec.rb +60 -0
  28. data/spec/git_helper/gitlab_client_spec.rb +26 -0
  29. data/spec/git_helper/highline_cli_spec.rb +215 -0
  30. data/spec/git_helper/local_code_spec.rb +231 -0
  31. data/spec/git_helper/merge_request_spec.rb +234 -0
  32. data/spec/git_helper/new_branch_spec.rb +44 -0
  33. data/spec/git_helper/octokit_client_spec.rb +26 -0
  34. data/spec/git_helper/pull_request_spec.rb +246 -0
  35. data/spec/spec_helper.rb +0 -7
  36. metadata +41 -24
@@ -1,18 +1,9 @@
1
- require_relative './highline_cli.rb'
2
-
3
1
  module GitHelper
4
2
  class NewBranch
5
3
  def execute(new_branch_name = nil)
6
- branch_name = new_branch_name || cli.ask('New branch name?')
4
+ branch_name = new_branch_name || GitHelper::HighlineCli.new.new_branch_name
7
5
  puts "Attempting to create a new branch: #{branch_name}"
8
- system("git pull")
9
- system("git branch --no-track #{branch_name}")
10
- system("git checkout #{branch_name}")
11
- system("git push --set-upstream origin #{branch_name}")
12
- end
13
-
14
- private def cli
15
- @cli ||= GitHelper::HighlineCli.new
6
+ GitHelper::LocalCode.new.new_branch(branch_name)
16
7
  end
17
8
  end
18
9
  end
@@ -1,5 +1,4 @@
1
1
  require 'octokit'
2
- require_relative './git_config_reader.rb'
3
2
 
4
3
  module GitHelper
5
4
  class OctokitClient
@@ -1,13 +1,19 @@
1
- require_relative './octokit_client.rb'
2
- require_relative './highline_cli.rb'
3
-
4
1
  module GitHelper
5
2
  class GitHubPullRequest
6
- def create
3
+ attr_accessor :local_repo, :local_branch, :local_code, :cli, :base_branch, :new_pr_title
4
+
5
+ def initialize(options)
6
+ @local_repo = options[:local_project]
7
+ @local_branch = options[:local_branch]
8
+ @local_code = options[:local_code]
9
+ @cli = options[:cli]
10
+ end
11
+
12
+ def create(options)
13
+ @base_branch = options[:base_branch]
14
+ @new_pr_title = options[:new_title]
15
+
7
16
  begin
8
- # Ask these questions right away
9
- base_branch
10
- new_pr_title
11
17
  new_pr_body
12
18
 
13
19
  puts "Creating pull request: #{new_pr_title}"
@@ -30,12 +36,11 @@ module GitHelper
30
36
 
31
37
  def merge
32
38
  begin
33
- # Ask these questions right away
34
39
  pr_id
35
40
  merge_method
36
41
 
37
42
  puts "Merging pull request: #{pr_id}"
38
- merge = octokit_client.merge_pull_request(local_repo, pr_id, existing_pr_title, { merge_method: merge_method })
43
+ merge = octokit_client.merge_pull_request(local_repo, pr_id, existing_pr.title, { merge_method: merge_method })
39
44
  puts "Pull request successfully merged: #{merge.sha}"
40
45
  rescue Octokit::UnprocessableEntity => e
41
46
  puts 'Could not merge pull request:'
@@ -66,131 +71,61 @@ module GitHelper
66
71
  end
67
72
  end
68
73
 
69
- private def local_repo
70
- # Get the repository by looking in the remote URLs for the full repository name
71
- remotes = `git remote -v`
72
- return remotes.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
73
- end
74
-
75
- private def local_branch
76
- # Get the current branch by looking in the list of branches for the *
77
- branches = `git branch`
78
- return branches.scan(/\*\s([\S]*)/).first.first
79
- end
80
-
81
- private def read_template
82
- if pr_template_options.count == 1
83
- apply_template?(pr_template_options.first) ? File.open(pr_template_options.first).read : ''
84
- else
85
- template_file_name_to_apply = template_to_apply
86
- template_file_name_to_apply == "None" ? '' : File.open(template_file_name_to_apply).read
87
- end
88
- end
89
-
90
- private def merge_options
91
- [ 'merge', 'squash', 'rebase' ]
92
- end
93
-
94
- private def pr_id
95
- @pr_id ||= cli.ask('Pull Request ID?')
96
- end
97
-
98
- private def existing_pr_title
99
- @existing_pr_title ||= octokit_client.pull_request(local_repo, pr_id).title
100
- end
101
-
102
- private def new_pr_title
103
- @new_pr_title ||= accept_autogenerated_title? ? autogenerated_title : cli.ask('Title?')
104
- end
105
-
106
74
  private def new_pr_body
107
- @new_pr_body ||= pr_template_options.empty? ? '' : read_template
108
- end
109
-
110
- private def base_branch
111
- @base_branch ||= base_branch_default? ? default_branch : cli.ask('Base branch?')
75
+ @new_pr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
112
76
  end
113
77
 
114
- private def autogenerated_title
115
- @autogenerated_title ||= generate_title
116
- end
117
-
118
- private def generate_title
119
- branch_arr = local_branch.split(local_branch.include?('_') ? '_' : '-')
120
-
121
- return if branch_arr.empty?
78
+ private def template_name_to_apply
79
+ return @template_name_to_apply if @template_name_to_apply
80
+ @template_name_to_apply = nil
122
81
 
123
- if branch_arr.length == 1
124
- return branch_arr.first.capitalize
125
- end
126
-
127
- if branch_arr[0].scan(/([\w]+)/).empty? || branch_arr[1].scan(/([\d]+)/).empty?
128
- branch_arr[0..-1].join(' ').capitalize
129
- else
130
- issue = branch_arr[0].upcase
131
-
132
- if issue.include?('-')
133
- description = branch_arr[1..-1].join(' ')
82
+ unless pr_template_options.empty?
83
+ if pr_template_options.count == 1
84
+ apply_single_template = cli.apply_template?(pr_template_options.first, 'pull')
85
+ @template_name_to_apply = pr_template_options.first if apply_single_template
134
86
  else
135
- issue = "#{issue}-#{branch_arr[1]}"
136
- description = branch_arr[2..-1].join(' ')
87
+ response = cli.template_to_apply(pr_template_options, 'pull')
88
+ @template_name_to_apply = response unless response == 'None'
137
89
  end
138
-
139
- "#{issue} #{description.capitalize}"
140
90
  end
141
- end
142
91
 
143
- private def default_branch
144
- @default_branch ||= octokit_client.repository(local_repo).default_branch
92
+ @template_name_to_apply
145
93
  end
146
94
 
147
- private def merge_method
148
- return @merge_method if @merge_method
149
- index = cli.ask_options("Merge method?", merge_options)
150
- @merge_method = merge_options[index]
95
+ private def pr_template_options
96
+ @pr_template_options ||= local_code.template_options({
97
+ nested_directory_name: 'PULL_REQUEST_TEMPLATE',
98
+ non_nested_file_name: 'pull_request_template'
99
+ })
151
100
  end
152
101
 
153
- private def template_to_apply
154
- return @template_to_apply if @template_to_apply
155
- complete_options = pr_template_options << 'None'
156
- index = cli.ask_options("Which pull request template should be applied?", complete_options)
157
- @template_to_apply = complete_options[index]
102
+ private def pr_id
103
+ @pr_id ||= cli.code_request_id('Pull')
158
104
  end
159
105
 
160
- private def pr_template_options
161
- return @pr_template_options if @pr_template_options
162
- nested_templates = Dir.glob(File.join("**/PULL_REQUEST_TEMPLATE", "*.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
163
- non_nested_templates = Dir.glob(File.join("**", "pull_request_template.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
164
- @pr_template_options = nested_templates.concat(non_nested_templates)
106
+ private def merge_method
107
+ @merge_method ||= merge_options.length == 1 ? merge_options.first : cli.merge_method(merge_options)
165
108
  end
166
109
 
167
- private def base_branch_default?
168
- answer = cli.ask("Is '#{default_branch}' the correct base branch for your new pull request? (y/n)")
169
- !!(answer =~ /^y/i)
110
+ private def merge_options
111
+ return @merge_options if @merge_options
112
+ merge_options = []
113
+ merge_options << 'merge' if existing_project.allow_merge_commit
114
+ merge_options << 'squash' if existing_project.allow_squash_merge
115
+ merge_options << 'rebase' if existing_project.allow_rebase_merge
116
+ @merge_options = merge_options
170
117
  end
171
118
 
172
- private def accept_autogenerated_title?
173
- title = autogenerated_title
174
-
175
- if title
176
- answer = cli.ask("Accept the autogenerated merge request title '#{title}'? (y/n)")
177
- !!(answer =~ /^y/i)
178
- else
179
- false
180
- end
119
+ private def existing_project
120
+ @existing_project ||= octokit_client.repository(local_repo)
181
121
  end
182
122
 
183
- private def apply_template?(template_file_name)
184
- answer = cli.ask("Apply the pull request template from #{template_file_name}? (y/n)")
185
- !!(answer =~ /^y/i)
123
+ private def existing_pr
124
+ @existing_pr ||= octokit_client.pull_request(local_repo, pr_id)
186
125
  end
187
126
 
188
127
  private def octokit_client
189
128
  @octokit_client ||= GitHelper::OctokitClient.new.client
190
129
  end
191
-
192
- private def cli
193
- @cli ||= GitHelper::HighlineCli.new
194
- end
195
130
  end
196
131
  end
@@ -1,3 +1,3 @@
1
1
  module GitHelper
2
- VERSION = '1.2.0'
2
+ VERSION = '2.0.2'
3
3
  end
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+ require 'git_helper'
3
+
4
+ describe GitHelper::ChangeRemote do
5
+ let(:remote1) { 'git@github.com:github-username-old/project-1.git' }
6
+ let(:local_code) do
7
+ double(:local_code,
8
+ remotes: [remote1],
9
+ remote_name: 'origin',
10
+ ssh_remote?: true,
11
+ https_remote?: false,
12
+ remote_project: 'project-1',
13
+ remote_source: 'github.com',
14
+ change_remote: true
15
+ )
16
+ end
17
+ let(:cli) { double(:highline_cli, process_directory_remotes?: true) }
18
+ let(:old_owner) { 'github-username-old' }
19
+ let(:new_owner) { 'github-username-new' }
20
+ let(:directory_entries) { [ '.', '..', 'project-1', 'project-2', 'project-3' ] }
21
+
22
+ subject { GitHelper::ChangeRemote.new(old_owner, new_owner) }
23
+
24
+ before do
25
+ allow(GitHelper::HighlineCli).to receive(:new).and_return(cli)
26
+ allow(GitHelper::LocalCode).to receive(:new).and_return(local_code)
27
+ end
28
+
29
+ describe '#execute' do
30
+ before do
31
+ allow(Dir).to receive(:pwd).and_return('/Users/firstname/lastname/path/to/project')
32
+ allow(Dir).to receive(:entries).and_return(directory_entries)
33
+ allow(File).to receive(:join).and_return('/Users/firstname/lastname/path/to/project/project-1')
34
+ allow(File).to receive(:directory?).and_return(true)
35
+ allow(subject).to receive(:process_dir)
36
+ end
37
+
38
+ it 'should call to process at least one directory' do
39
+ expect(subject).to receive(:process_dir).at_least(:once)
40
+ subject.execute
41
+ end
42
+
43
+ it 'should definitely look in the file structure' do
44
+ expect(Dir).to receive(:pwd)
45
+ expect(Dir).to receive(:entries)
46
+ expect(File).to receive(:join)
47
+ expect(File).to receive(:directory?)
48
+ subject.execute
49
+ end
50
+ end
51
+
52
+ describe '#process_dir' do
53
+ before do
54
+ allow(Dir).to receive(:chdir).and_return(nil)
55
+ allow(File).to receive(:exist?).and_return(true)
56
+ allow(subject).to receive(:process_git_repository)
57
+ end
58
+
59
+ it 'should definitely look in the file structure' do
60
+ expect(Dir).to receive(:chdir)
61
+ expect(File).to receive(:exist?)
62
+ subject.send(:process_dir, '/Users/firstname/lastname/path/to/project', '/Users/firstname/lastname/path/to/project/project-1')
63
+ end
64
+
65
+ context 'when the user says to process the directory' do
66
+ it 'should call to process the git repository at least once' do
67
+ expect(subject).to receive(:process_git_repository).at_least(:once)
68
+ subject.send(:process_dir, '/Users/firstname/lastname/path/to/project', '/Users/firstname/lastname/path/to/project/project-1')
69
+ end
70
+ end
71
+
72
+ context 'when the user says not to process the directory' do
73
+ let(:cli) { double(:highline_cli, process_directory_remotes?: false) }
74
+
75
+ it 'should not call to process the directory' do
76
+ expect(subject).not_to receive(:process_git_repository)
77
+ subject.send(:process_dir, '/Users/firstname/lastname/path/to/project', '/Users/firstname/lastname/path/to/project/project-1')
78
+ end
79
+ end
80
+ end
81
+
82
+ describe '#process_git_repository' do
83
+ before do
84
+ allow(subject).to receive(:process_remote).and_return(nil)
85
+ end
86
+
87
+ it 'should call local_code' do
88
+ expect(GitHelper::LocalCode).to receive(:new)
89
+ subject.send(:process_git_repository)
90
+ end
91
+
92
+ context 'when the remote includes the old owner' do
93
+ it 'should call to process the remote' do
94
+ expect(subject).to receive(:process_remote)
95
+ subject.send(:process_git_repository)
96
+ end
97
+ end
98
+
99
+ context 'when the remote does not include the old owner' do
100
+ let(:remote1) { 'git@github.com:github-username-new/project-1.git' }
101
+
102
+ it 'should not call to process the remote' do
103
+ expect(subject).not_to receive(:process_remote)
104
+ subject.send(:process_git_repository)
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#process_remote' do
110
+ it 'should always get the remote name' do
111
+ expect(local_code).to receive(:remote_name)
112
+ subject.send(:process_remote, remote1)
113
+ end
114
+
115
+ it 'should always attempt to change the remote' do
116
+ expect(local_code).to receive(:change_remote)
117
+ subject.send(:process_remote, remote1)
118
+ end
119
+
120
+ it 'should attempt to get the remote repo exactly once' do
121
+ expect(local_code).to receive(:remote_project).exactly(:once)
122
+ subject.send(:process_remote, remote1)
123
+ end
124
+
125
+ it 'should attempt to get the remote source exactly once' do
126
+ expect(local_code).to receive(:remote_source).exactly(:once)
127
+ subject.send(:process_remote, remote1)
128
+ end
129
+
130
+ it 'should ask if the remote is SSH' do
131
+ expect(local_code).to receive(:ssh_remote?)
132
+ subject.send(:process_remote, remote1)
133
+ end
134
+
135
+ context 'https remote' do
136
+ let(:local_code) do
137
+ double(:local_code,
138
+ remotes: [remote1],
139
+ remote_name: 'origin',
140
+ ssh_remote?: false,
141
+ https_remote?: false,
142
+ remote_project: 'project-1',
143
+ remote_source: 'github.com',
144
+ change_remote: true
145
+ )
146
+ end
147
+
148
+ it 'should ask if the remote is SSH' do
149
+ expect(local_code).to receive(:ssh_remote?)
150
+ subject.send(:process_remote, remote1)
151
+ end
152
+
153
+ it 'should ask if the remote is https' do
154
+ expect(local_code).to receive(:https_remote?)
155
+ subject.send(:process_remote, remote1)
156
+ end
157
+ end
158
+ end
159
+
160
+ describe '#local_code' do
161
+ it 'should create a new local code instance' do
162
+ expect(GitHelper::LocalCode).to receive(:new)
163
+ subject.send(:local_code)
164
+ end
165
+ end
166
+
167
+ describe '#cli' do
168
+ it 'should create a new highline CLI instance' do
169
+ expect(GitHelper::HighlineCli).to receive(:new)
170
+ subject.send(:cli)
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'git_helper'
3
+
4
+ describe GitHelper::CheckoutDefault do
5
+ let(:local_code) { double(:local_code, checkout_default: :done) }
6
+
7
+ subject { GitHelper::CheckoutDefault.new }
8
+
9
+ it 'should call GitHelper::LocalCode' do
10
+ expect(GitHelper::LocalCode).to receive(:new).and_return(local_code)
11
+ subject.execute
12
+ end
13
+
14
+ it 'should call the checkout_default method from the local code class' do
15
+ allow(GitHelper::LocalCode).to receive(:new).and_return(local_code)
16
+ expect(local_code).to receive(:checkout_default)
17
+ subject.execute
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'git_helper'
3
+
4
+ describe GitHelper::CleanBranches do
5
+ let(:local_code) { double(:local_code, clean_branches: :commit) }
6
+
7
+ subject { GitHelper::CleanBranches.new }
8
+
9
+ it 'should call GitHelper::LocalCode' do
10
+ expect(GitHelper::LocalCode).to receive(:new).and_return(local_code)
11
+ subject.execute
12
+ end
13
+
14
+ it 'should call the clean_branches method from the local code class' do
15
+ allow(GitHelper::LocalCode).to receive(:new).and_return(local_code)
16
+ expect(local_code).to receive(:clean_branches)
17
+ subject.execute
18
+ end
19
+ end