git_helper 1.2.0 → 2.0.2

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/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