git_helper 3.2.0 → 3.3.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.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +131 -0
- data/Guardfile +3 -1
- data/README.md +11 -6
- data/Rakefile +2 -0
- data/bin/git-helper +25 -13
- data/lib/git_helper.rb +5 -1
- data/lib/git_helper/change_remote.rb +11 -3
- data/lib/git_helper/checkout_default.rb +2 -0
- data/lib/git_helper/clean_branches.rb +2 -0
- data/lib/git_helper/code_request.rb +29 -15
- data/lib/git_helper/empty_commit.rb +2 -0
- data/lib/git_helper/forget_local_commits.rb +2 -0
- data/lib/git_helper/git_config_reader.rb +12 -6
- data/lib/git_helper/gitlab_client.rb +2 -0
- data/lib/git_helper/highline_cli.rb +15 -69
- data/lib/git_helper/local_code.rb +18 -8
- data/lib/git_helper/merge_request.rb +88 -68
- data/lib/git_helper/new_branch.rb +3 -1
- data/lib/git_helper/octokit_client.rb +2 -0
- data/lib/git_helper/pull_request.rb +95 -64
- data/lib/git_helper/setup.rb +116 -0
- data/lib/git_helper/version.rb +3 -1
- data/spec/git_helper/change_remote_spec.rb +19 -19
- data/spec/git_helper/checkout_default_spec.rb +2 -0
- data/spec/git_helper/clean_branches_spec.rb +2 -0
- data/spec/git_helper/code_request_spec.rb +27 -24
- data/spec/git_helper/empty_commit_spec.rb +2 -0
- data/spec/git_helper/forget_local_commits_spec.rb +2 -0
- data/spec/git_helper/git_config_reader_spec.rb +32 -4
- data/spec/git_helper/gitlab_client_spec.rb +2 -0
- data/spec/git_helper/highline_cli_spec.rb +21 -185
- data/spec/git_helper/local_code_spec.rb +2 -0
- data/spec/git_helper/merge_request_spec.rb +22 -21
- data/spec/git_helper/new_branch_spec.rb +4 -2
- data/spec/git_helper/octokit_client_spec.rb +2 -0
- data/spec/git_helper/pull_request_spec.rb +17 -15
- data/spec/git_helper/setup_spec.rb +180 -0
- data/spec/spec_helper.rb +3 -1
- metadata +22 -5
@@ -1,23 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GitHelper
|
2
4
|
class GitConfigReader
|
3
5
|
def gitlab_token
|
4
6
|
config_file[:gitlab_token]
|
5
7
|
end
|
6
8
|
|
9
|
+
def gitlab_user
|
10
|
+
config_file[:gitlab_user]
|
11
|
+
end
|
12
|
+
|
7
13
|
def github_token
|
8
14
|
config_file[:github_token]
|
9
15
|
end
|
10
16
|
|
11
|
-
|
12
|
-
|
17
|
+
def github_user
|
18
|
+
config_file[:github_user]
|
13
19
|
end
|
14
20
|
|
15
|
-
|
16
|
-
Dir.pwd.scan(/\
|
21
|
+
def git_config_file_path
|
22
|
+
"#{Dir.pwd.scan(%r{\A/\w*/\w*/}).first}.git_helper/config.yml"
|
17
23
|
end
|
18
24
|
|
19
|
-
private def
|
20
|
-
|
25
|
+
private def config_file
|
26
|
+
YAML.load_file(git_config_file_path)
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
@@ -1,85 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GitHelper
|
2
4
|
class HighlineCli
|
3
|
-
def
|
4
|
-
ask(
|
5
|
-
|
6
|
-
|
7
|
-
def process_directory_remotes?(directory)
|
8
|
-
answer = ask("Found git directory: #{directory}. Do you wish to proceed in updating #{directory}'s remote URLs? (y/n)")
|
9
|
-
answer.empty? ? true : !!(answer =~ /^y/i)
|
10
|
-
end
|
11
|
-
|
12
|
-
def conflicting_remote_clarification
|
13
|
-
ask('Found git remotes for both GitHub and GitLab. Would you like to proceed with GitLab or GitHub? (github/gitlab)').downcase
|
14
|
-
end
|
15
|
-
|
16
|
-
def title
|
17
|
-
ask('Title?')
|
18
|
-
end
|
19
|
-
|
20
|
-
def base_branch
|
21
|
-
ask('Base branch?')
|
22
|
-
end
|
23
|
-
|
24
|
-
def code_request_id(request_type)
|
25
|
-
ask("#{request_type} Request ID?")
|
26
|
-
end
|
27
|
-
|
28
|
-
def accept_autogenerated_title?(autogenerated_title)
|
29
|
-
return false unless autogenerated_title
|
30
|
-
answer = ask("Accept the autogenerated code request title '#{autogenerated_title}'? (y/n)")
|
31
|
-
answer.empty? ? true : !!(answer =~ /^y/i)
|
32
|
-
end
|
33
|
-
|
34
|
-
def base_branch_default?(default_branch)
|
35
|
-
answer = ask("Is '#{default_branch}' the correct base branch for your new code request? (y/n)")
|
36
|
-
answer.empty? ? true : !!(answer =~ /^y/i)
|
37
|
-
end
|
38
|
-
|
39
|
-
def squash_merge_request?
|
40
|
-
answer = ask('Squash merge request? (y/n)')
|
41
|
-
answer.empty? ? true : !!(answer =~ /^y/i)
|
42
|
-
end
|
43
|
-
|
44
|
-
def remove_source_branch?
|
45
|
-
answer = ask('Remove source branch after merging? (y/n)')
|
46
|
-
answer.empty? ? true : !!(answer =~ /^y/i)
|
47
|
-
end
|
48
|
-
|
49
|
-
def merge_method(merge_options)
|
50
|
-
index = ask_options("Merge method?", merge_options)
|
51
|
-
merge_options[index]
|
52
|
-
end
|
53
|
-
|
54
|
-
def apply_template?(template_file_name, request_type)
|
55
|
-
answer = ask("Apply the #{request_type} request template from #{template_file_name}? (y/n)")
|
56
|
-
answer.empty? ? true : !!(answer =~ /^y/i)
|
57
|
-
end
|
58
|
-
|
59
|
-
def template_to_apply(template_options, request_type)
|
60
|
-
complete_options = template_options << 'None'
|
61
|
-
index = ask_options("Which #{request_type} request template should be applied?", complete_options)
|
62
|
-
complete_options[index]
|
5
|
+
def ask(prompt)
|
6
|
+
highline_client.ask(prompt) do |conf|
|
7
|
+
conf.readline = true
|
8
|
+
end.to_s
|
63
9
|
end
|
64
10
|
|
65
|
-
|
66
|
-
|
67
|
-
#######################
|
68
|
-
|
69
|
-
private def ask(prompt)
|
70
|
-
highline_client.ask(prompt) do |conf|
|
11
|
+
def ask_yes_no(prompt)
|
12
|
+
answer = highline_client.ask(prompt) do |conf|
|
71
13
|
conf.readline = true
|
72
14
|
end.to_s
|
15
|
+
|
16
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
73
17
|
end
|
74
18
|
|
75
|
-
|
76
|
-
choices_as_string_options = ''
|
19
|
+
def ask_options(prompt, choices)
|
20
|
+
choices_as_string_options = ''.dup
|
77
21
|
choices.each { |choice| choices_as_string_options << "#{choices.index(choice) + 1}. #{choice}\n" }
|
78
22
|
compiled_prompt = "#{prompt}\n#{choices_as_string_options.strip}"
|
79
23
|
|
80
|
-
highline_client.ask(compiled_prompt) do |conf|
|
24
|
+
index = highline_client.ask(compiled_prompt) do |conf|
|
81
25
|
conf.readline = true
|
82
26
|
end.to_i - 1
|
27
|
+
|
28
|
+
choices[index]
|
83
29
|
end
|
84
30
|
|
85
31
|
private def highline_client
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GitHelper
|
2
4
|
class LocalCode
|
3
5
|
def checkout_default
|
@@ -44,20 +46,20 @@ module GitHelper
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def https_remote?(remote)
|
47
|
-
remote.scan(
|
49
|
+
remote.scan(%r{(https://)}).any?
|
48
50
|
end
|
49
51
|
|
50
52
|
def remote_project(remote)
|
51
53
|
if https_remote?(remote)
|
52
|
-
remote.scan(
|
54
|
+
remote.scan(%r{https://\S+/(\S*).git}).first.first
|
53
55
|
elsif ssh_remote?(remote)
|
54
|
-
remote.scan(
|
56
|
+
remote.scan(%r{/(\S*).git}).first.first
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
def remote_source(remote)
|
59
61
|
if https_remote?(remote)
|
60
|
-
remote.scan(
|
62
|
+
remote.scan(%r{https://([a-zA-z.]+)/}).first.first
|
61
63
|
elsif ssh_remote?(remote)
|
62
64
|
remote.scan(/git@([a-zA-z.]+):/).first.first
|
63
65
|
end
|
@@ -73,18 +75,20 @@ module GitHelper
|
|
73
75
|
|
74
76
|
def project_name
|
75
77
|
# Get the repo/project name by looking in the remote URLs for the full name
|
76
|
-
`git remote -v`.scan(/\S
|
78
|
+
`git remote -v`.scan(/\S\s*\S+.com\S{1}(\S*).git/).first.first
|
77
79
|
end
|
78
80
|
|
79
81
|
def branch
|
80
82
|
# Get the current branch by looking in the list of branches for the *
|
81
|
-
`git branch`.scan(/\*\s(
|
83
|
+
`git branch`.scan(/\*\s(\S*)/).first.first
|
82
84
|
end
|
83
85
|
|
84
86
|
def default_branch
|
85
87
|
`git symbolic-ref refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@" | tr -d "\n"`
|
86
88
|
end
|
87
89
|
|
90
|
+
# rubocop:disable Metrics/AbcSize
|
91
|
+
# rubocop:disable Metrics/MethodLength
|
88
92
|
def template_options(identifiers)
|
89
93
|
nested_templates = Dir.glob(
|
90
94
|
File.join("#{identifiers[:template_directory]}/#{identifiers[:nested_directory_name]}", '*.md'),
|
@@ -100,11 +104,15 @@ module GitHelper
|
|
100
104
|
)
|
101
105
|
nested_templates.concat(non_nested_templates).concat(root_templates).uniq
|
102
106
|
end
|
107
|
+
# rubocop:enable Metrics/MethodLength
|
108
|
+
# rubocop:enable Metrics/AbcSize
|
103
109
|
|
104
110
|
def read_template(file_name)
|
105
111
|
File.open(file_name).read
|
106
112
|
end
|
107
113
|
|
114
|
+
# rubocop:disable Metrics/AbcSize
|
115
|
+
# rubocop:disable Metrics/MethodLength
|
108
116
|
def generate_title(local_branch)
|
109
117
|
branch_arr = local_branch.split(local_branch.include?('_') ? '_' : '-')
|
110
118
|
|
@@ -112,11 +120,11 @@ module GitHelper
|
|
112
120
|
|
113
121
|
if branch_arr.length == 1
|
114
122
|
branch_arr.first.capitalize
|
115
|
-
elsif branch_arr[0].scan(/(
|
123
|
+
elsif branch_arr[0].scan(/(\w+)/).any? && branch_arr[1].scan(/(\d+)/).any? # branch includes jira_123 at beginning
|
116
124
|
issue = "#{branch_arr[0].upcase}-#{branch_arr[1]}"
|
117
125
|
description = branch_arr[2..-1].join(' ')
|
118
126
|
"#{issue} #{description.capitalize}"
|
119
|
-
elsif branch_arr[0].scan(/(
|
127
|
+
elsif branch_arr[0].scan(/(\w+-\d+)/).any? # branch includes string jira-123 at beginning
|
120
128
|
issue = branch_arr[0].upcase
|
121
129
|
description = branch_arr[1..-1].join(' ')
|
122
130
|
"#{issue} #{description.capitalize}"
|
@@ -124,5 +132,7 @@ module GitHelper
|
|
124
132
|
branch_arr[0..-1].join(' ').capitalize
|
125
133
|
end
|
126
134
|
end
|
135
|
+
# rubocop:enable Metrics/MethodLength
|
136
|
+
# rubocop:enable Metrics/AbcSize
|
127
137
|
end
|
128
138
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GitHelper
|
2
4
|
class GitLabMergeRequest
|
3
5
|
attr_accessor :local_project, :local_branch, :local_code, :cli, :base_branch, :new_mr_title
|
@@ -9,70 +11,74 @@ module GitHelper
|
|
9
11
|
@cli = options[:cli]
|
10
12
|
end
|
11
13
|
|
14
|
+
# rubocop:disable Metrics/MethodLength
|
15
|
+
# rubocop:disable Metrics/AbcSize
|
12
16
|
def create(options)
|
13
17
|
@base_branch = options[:base_branch]
|
14
18
|
@new_mr_title = options[:new_title]
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
puts "Merge request successfully created: #{mr.web_url}"
|
32
|
-
end
|
33
|
-
rescue Gitlab::Error::Conflict => e
|
34
|
-
puts 'Could not create merge request:'
|
35
|
-
puts ' A merge request already exists for this branch'
|
36
|
-
rescue Exception => e
|
37
|
-
puts 'Could not create merge request:'
|
38
|
-
puts e.message
|
20
|
+
options = {
|
21
|
+
source_branch: local_branch,
|
22
|
+
target_branch: base_branch,
|
23
|
+
squash: squash_merge_request,
|
24
|
+
remove_source_branch: remove_source_branch,
|
25
|
+
description: new_mr_body
|
26
|
+
}
|
27
|
+
|
28
|
+
puts "Creating merge request: #{new_mr_title}"
|
29
|
+
mr = gitlab_client.create_merge_request(local_project, new_mr_title, options)
|
30
|
+
|
31
|
+
if mr.diff_refs.base_sha == mr.diff_refs.head_sha
|
32
|
+
puts "Merge request was created, but no commits have been pushed to GitLab: #{mr.web_url}"
|
33
|
+
else
|
34
|
+
puts "Merge request successfully created: #{mr.web_url}"
|
39
35
|
end
|
36
|
+
rescue Gitlab::Error::Conflict
|
37
|
+
puts 'Could not create merge request:'
|
38
|
+
puts ' A merge request already exists for this branch'
|
39
|
+
rescue StandardError => e
|
40
|
+
puts 'Could not create merge request:'
|
41
|
+
puts e.message
|
40
42
|
end
|
43
|
+
# rubocop:enable Metrics/AbcSize
|
44
|
+
# rubocop:enable Metrics/MethodLength
|
41
45
|
|
46
|
+
# rubocop:disable Metrics/MethodLength
|
47
|
+
# rubocop:disable Metrics/AbcSize
|
42
48
|
def merge
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
mr_id
|
50
|
+
options = {
|
51
|
+
should_remove_source_branch: existing_mr.should_remove_source_branch || existing_mr.force_remove_source_branch,
|
52
|
+
squash: existing_mr.squash,
|
53
|
+
squash_commit_message: existing_mr.title
|
54
|
+
}
|
55
|
+
|
56
|
+
puts "Merging merge request: #{mr_id}"
|
57
|
+
merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
|
58
|
+
|
59
|
+
if merge.merge_commit_sha.nil?
|
60
|
+
options[:squash] = true
|
52
61
|
merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
|
62
|
+
end
|
53
63
|
|
54
|
-
|
55
|
-
options[:squash] = true
|
56
|
-
merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
|
57
|
-
end
|
58
|
-
|
59
|
-
if merge.merge_commit_sha.nil?
|
60
|
-
puts 'Could not merge merge request:'
|
61
|
-
puts " #{merge.merge_error}"
|
62
|
-
else
|
63
|
-
puts "Merge request successfully merged: #{merge.merge_commit_sha}"
|
64
|
-
end
|
65
|
-
rescue Gitlab::Error::MethodNotAllowed => e
|
66
|
-
puts 'Could not merge merge request:'
|
67
|
-
puts ' The merge request is not mergeable'
|
68
|
-
rescue Gitlab::Error::NotFound => e
|
64
|
+
if merge.merge_commit_sha.nil?
|
69
65
|
puts 'Could not merge merge request:'
|
70
|
-
puts "
|
71
|
-
|
72
|
-
puts
|
73
|
-
puts e.message
|
66
|
+
puts " #{merge.merge_error}"
|
67
|
+
else
|
68
|
+
puts "Merge request successfully merged: #{merge.merge_commit_sha}"
|
74
69
|
end
|
70
|
+
rescue Gitlab::Error::MethodNotAllowed
|
71
|
+
puts 'Could not merge merge request:'
|
72
|
+
puts ' The merge request is not mergeable'
|
73
|
+
rescue Gitlab::Error::NotFound
|
74
|
+
puts 'Could not merge merge request:'
|
75
|
+
puts " Could not a locate a merge request to merge with ID #{mr_id}"
|
76
|
+
rescue StandardError => e
|
77
|
+
puts 'Could not merge merge request:'
|
78
|
+
puts e.message
|
75
79
|
end
|
80
|
+
# rubocop:enable Metrics/AbcSize
|
81
|
+
# rubocop:enable Metrics/MethodLength
|
76
82
|
|
77
83
|
private def new_mr_body
|
78
84
|
@new_mr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
|
@@ -80,39 +86,53 @@ module GitHelper
|
|
80
86
|
|
81
87
|
private def template_name_to_apply
|
82
88
|
return @template_name_to_apply if @template_name_to_apply
|
89
|
+
|
83
90
|
@template_name_to_apply = nil
|
84
91
|
|
85
|
-
unless mr_template_options.empty?
|
86
|
-
if mr_template_options.count == 1
|
87
|
-
apply_single_template = cli.apply_template?(mr_template_options.first, 'merge')
|
88
|
-
@template_name_to_apply = mr_template_options.first if apply_single_template
|
89
|
-
else
|
90
|
-
response = cli.template_to_apply(mr_template_options, 'merge')
|
91
|
-
@template_name_to_apply = response unless response == 'None'
|
92
|
-
end
|
93
|
-
end
|
92
|
+
determine_template unless mr_template_options.empty?
|
94
93
|
|
95
94
|
@template_name_to_apply
|
96
95
|
end
|
97
96
|
|
97
|
+
# rubocop:disable Metrics/MethodLength
|
98
|
+
private def determine_template
|
99
|
+
if mr_template_options.count == 1
|
100
|
+
apply_single_template = cli.ask_yes_no(
|
101
|
+
"Apply the merge request template from #{mr_template_options.first}? (y/n)"
|
102
|
+
)
|
103
|
+
@template_name_to_apply = mr_template_options.first if apply_single_template
|
104
|
+
else
|
105
|
+
response = cli.ask_options(
|
106
|
+
'Which merge request template should be applied?', mr_template_options << 'None'
|
107
|
+
)
|
108
|
+
@template_name_to_apply = response unless response == 'None'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
# rubocop:enable Metrics/MethodLength
|
112
|
+
|
98
113
|
private def mr_template_options
|
99
|
-
@mr_template_options ||= local_code.template_options(
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
114
|
+
@mr_template_options ||= local_code.template_options(
|
115
|
+
{
|
116
|
+
template_directory: '.gitlab',
|
117
|
+
nested_directory_name: 'merge_request_templates',
|
118
|
+
non_nested_file_name: 'merge_request_template'
|
119
|
+
}
|
120
|
+
)
|
104
121
|
end
|
105
122
|
|
106
123
|
private def mr_id
|
107
|
-
@mr_id ||= cli.
|
124
|
+
@mr_id ||= cli.ask('Merge Request ID?')
|
108
125
|
end
|
109
126
|
|
110
127
|
private def squash_merge_request
|
111
|
-
@squash_merge_request ||= cli.
|
128
|
+
@squash_merge_request ||= cli.ask_yes_no('Squash merge request? (y/n)')
|
112
129
|
end
|
113
130
|
|
114
131
|
private def remove_source_branch
|
115
|
-
@remove_source_branch ||=
|
132
|
+
@remove_source_branch ||=
|
133
|
+
existing_project.remove_source_branch_after_merge || cli.ask_yes_no(
|
134
|
+
'Remove source branch after merging? (y/n)'
|
135
|
+
)
|
116
136
|
end
|
117
137
|
|
118
138
|
private def existing_project
|