git_helper 1.3.0 → 3.0.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +17 -13
- data/README.md +37 -44
- data/Rakefile +1 -37
- data/bin/git-helper +10 -28
- data/lib/git_helper.rb +3 -5
- data/lib/git_helper/change_remote.rb +53 -40
- data/lib/git_helper/checkout_default.rb +1 -1
- data/lib/git_helper/clean_branches.rb +1 -4
- data/lib/git_helper/code_request.rb +95 -0
- data/lib/git_helper/empty_commit.rb +1 -1
- data/lib/git_helper/forget_local_commits.rb +7 -0
- data/lib/git_helper/git_config_reader.rb +1 -1
- data/lib/git_helper/gitlab_client.rb +0 -1
- data/lib/git_helper/highline_cli.rb +21 -17
- data/lib/git_helper/local_code.rb +67 -19
- data/lib/git_helper/merge_request.rb +55 -73
- data/lib/git_helper/new_branch.rb +2 -13
- data/lib/git_helper/octokit_client.rb +0 -1
- data/lib/git_helper/pull_request.rb +44 -67
- data/lib/git_helper/version.rb +1 -1
- data/plugins.zip +0 -0
- data/spec/git_helper/change_remote_spec.rb +173 -0
- data/spec/git_helper/checkout_default_spec.rb +19 -0
- data/spec/git_helper/clean_branches_spec.rb +19 -0
- data/spec/git_helper/code_request_spec.rb +259 -0
- data/spec/git_helper/empty_commit_spec.rb +19 -0
- data/spec/git_helper/forget_local_commits_spec.rb +19 -0
- data/spec/git_helper/git_config_reader_spec.rb +60 -0
- data/spec/git_helper/gitlab_client_spec.rb +26 -0
- data/spec/git_helper/highline_cli_spec.rb +215 -0
- data/spec/git_helper/local_code_spec.rb +231 -0
- data/spec/git_helper/merge_request_spec.rb +234 -0
- data/spec/git_helper/new_branch_spec.rb +44 -0
- data/spec/git_helper/octokit_client_spec.rb +26 -0
- data/spec/git_helper/pull_request_spec.rb +246 -0
- data/spec/spec_helper.rb +0 -7
- metadata +41 -24
@@ -1,10 +1,7 @@
|
|
1
1
|
module GitHelper
|
2
2
|
class CleanBranches
|
3
3
|
def execute
|
4
|
-
|
5
|
-
system("git pull")
|
6
|
-
system("git fetch -p")
|
7
|
-
system("git branch -vv | grep \"origin/.*: gone]\" | awk '{print \$1}' | grep -v \"*\" | xargs git branch -D")
|
4
|
+
GitHelper::LocalCode.new.clean_branches
|
8
5
|
end
|
9
6
|
end
|
10
7
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module GitHelper
|
2
|
+
class CodeRequest
|
3
|
+
def create
|
4
|
+
process_project.create({
|
5
|
+
base_branch: base_branch,
|
6
|
+
new_title: new_code_request_title
|
7
|
+
})
|
8
|
+
end
|
9
|
+
|
10
|
+
def merge
|
11
|
+
process_project.merge
|
12
|
+
end
|
13
|
+
|
14
|
+
private def process_project
|
15
|
+
if local_code.github_repo? # GitHub remotes are found
|
16
|
+
# If GitLab remotes are also found, ask for clarification, else proceed with GitHub
|
17
|
+
local_code.gitlab_project? ? ask_for_clarification : github_pull_request
|
18
|
+
elsif local_code.gitlab_project? # Only GitLab remotes are found
|
19
|
+
gitlab_merge_request
|
20
|
+
else # Neither GitHub nor GitLab remotes are found
|
21
|
+
puts 'Could not locate GitHub or GitLab remote URLs.'
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private def ask_for_clarification
|
27
|
+
resp = cli.conflicting_remote_clarification
|
28
|
+
if resp.include?('hub')
|
29
|
+
github_pull_request
|
30
|
+
elsif resp.include?('lab')
|
31
|
+
gitlab_merge_request
|
32
|
+
else
|
33
|
+
puts 'The answer we received was not parseable.'
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private def github_pull_request
|
39
|
+
@github_pull_request ||= GitHelper::GitHubPullRequest.new(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
private def gitlab_merge_request
|
43
|
+
@gitlab_merge_request ||= GitHelper::GitLabMergeRequest.new(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
private def options
|
47
|
+
{
|
48
|
+
local_project: local_project,
|
49
|
+
local_branch: local_branch,
|
50
|
+
local_code: local_code,
|
51
|
+
cli: cli
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
private def local_project
|
56
|
+
@local_project ||= local_code.project_name
|
57
|
+
end
|
58
|
+
|
59
|
+
private def default_branch
|
60
|
+
@default_branch ||= local_code.default_branch
|
61
|
+
end
|
62
|
+
|
63
|
+
private def base_branch
|
64
|
+
@base_branch ||= if cli.base_branch_default?(default_branch)
|
65
|
+
default_branch
|
66
|
+
else
|
67
|
+
cli.base_branch
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private def local_branch
|
72
|
+
@local_branch ||= local_code.branch
|
73
|
+
end
|
74
|
+
|
75
|
+
private def autogenerated_title
|
76
|
+
@autogenerated_title ||= local_code.generate_title(local_branch)
|
77
|
+
end
|
78
|
+
|
79
|
+
private def new_code_request_title
|
80
|
+
@new_code_request_title ||= if cli.accept_autogenerated_title?(autogenerated_title)
|
81
|
+
autogenerated_title
|
82
|
+
else
|
83
|
+
cli.title
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private def cli
|
88
|
+
@cli ||= GitHelper::HighlineCli.new
|
89
|
+
end
|
90
|
+
|
91
|
+
private def local_code
|
92
|
+
@local_code ||= GitHelper::LocalCode.new
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -6,6 +6,15 @@ module GitHelper
|
|
6
6
|
ask('New branch name?')
|
7
7
|
end
|
8
8
|
|
9
|
+
def process_directory_remotes?(directory)
|
10
|
+
answer = ask("Found git directory: #{directory}. Do you wish to proceed in updating #{directory}'s remote URLs? (y/n)")
|
11
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
12
|
+
end
|
13
|
+
|
14
|
+
def conflicting_remote_clarification
|
15
|
+
ask('Found git remotes for both GitHub and GitLab. Would you like to proceed with GitLab or GitHub? (github/gitlab)').downcase
|
16
|
+
end
|
17
|
+
|
9
18
|
def title
|
10
19
|
ask('Title?')
|
11
20
|
end
|
@@ -14,44 +23,39 @@ module GitHelper
|
|
14
23
|
ask('Base branch?')
|
15
24
|
end
|
16
25
|
|
17
|
-
def
|
18
|
-
ask(
|
19
|
-
end
|
20
|
-
|
21
|
-
def pull_request_id
|
22
|
-
ask('Pull Request ID?')
|
26
|
+
def code_request_id(request_type)
|
27
|
+
ask("#{request_type} Request ID?")
|
23
28
|
end
|
24
29
|
|
25
30
|
def accept_autogenerated_title?(autogenerated_title)
|
26
31
|
return false unless autogenerated_title
|
27
|
-
answer = ask("Accept the autogenerated
|
28
|
-
!!(answer =~ /^y/i)
|
32
|
+
answer = ask("Accept the autogenerated code request title '#{autogenerated_title}'? (y/n)")
|
33
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
29
34
|
end
|
30
35
|
|
31
36
|
def base_branch_default?(default_branch)
|
32
|
-
answer = ask("Is '#{default_branch}' the correct base branch for your new
|
33
|
-
!!(answer =~ /^y/i)
|
37
|
+
answer = ask("Is '#{default_branch}' the correct base branch for your new code request? (y/n)")
|
38
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
34
39
|
end
|
35
40
|
|
36
41
|
def squash_merge_request?
|
37
42
|
answer = ask('Squash merge request? (y/n)')
|
38
|
-
!!(answer =~ /^y/i)
|
43
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
39
44
|
end
|
40
45
|
|
41
46
|
def remove_source_branch?
|
42
47
|
answer = ask('Remove source branch after merging? (y/n)')
|
43
|
-
!!(answer =~ /^y/i)
|
48
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
44
49
|
end
|
45
50
|
|
46
|
-
def merge_method
|
47
|
-
merge_options = [ 'merge', 'squash', 'rebase' ]
|
51
|
+
def merge_method(merge_options)
|
48
52
|
index = ask_options("Merge method?", merge_options)
|
49
53
|
merge_options[index]
|
50
54
|
end
|
51
55
|
|
52
|
-
def apply_template?(template_file_name)
|
53
|
-
answer = ask("Apply the
|
54
|
-
!!(answer =~ /^y/i)
|
56
|
+
def apply_template?(template_file_name, request_type)
|
57
|
+
answer = ask("Apply the #{request_type} request template from #{template_file_name}? (y/n)")
|
58
|
+
answer.empty? ? true : !!(answer =~ /^y/i)
|
55
59
|
end
|
56
60
|
|
57
61
|
def template_to_apply(template_options, request_type)
|
@@ -1,5 +1,25 @@
|
|
1
1
|
module GitHelper
|
2
2
|
class LocalCode
|
3
|
+
def checkout_default
|
4
|
+
system("git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed \"s@^refs/remotes/origin/@@\")")
|
5
|
+
end
|
6
|
+
|
7
|
+
def forget_local_commits
|
8
|
+
system("git pull")
|
9
|
+
system("git reset --hard origin/HEAD")
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty_commit
|
13
|
+
system("git commit --allow-empty -m \"Empty commit\"")
|
14
|
+
end
|
15
|
+
|
16
|
+
def clean_branches
|
17
|
+
system("git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed \"s@^refs/remotes/origin/@@\")")
|
18
|
+
system("git pull")
|
19
|
+
system("git fetch -p")
|
20
|
+
system("git branch -vv | grep \"origin/.*: gone]\" | awk '{print \$1}' | grep -v \"*\" | xargs git branch -D")
|
21
|
+
end
|
22
|
+
|
3
23
|
def new_branch(branch_name)
|
4
24
|
system("git pull")
|
5
25
|
system("git branch --no-track #{branch_name}")
|
@@ -7,7 +27,51 @@ module GitHelper
|
|
7
27
|
system("git push --set-upstream origin #{branch_name}")
|
8
28
|
end
|
9
29
|
|
10
|
-
def
|
30
|
+
def change_remote(remote_name, remote_url)
|
31
|
+
`git remote set-url #{remote_name} #{remote_url}`
|
32
|
+
end
|
33
|
+
|
34
|
+
def remotes
|
35
|
+
`git remote -v`.split("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def remote_name(remote)
|
39
|
+
remote.scan(/([a-zA-z]+)/).first.first
|
40
|
+
end
|
41
|
+
|
42
|
+
def ssh_remote?(remote)
|
43
|
+
remote.scan(/(git@)/).any?
|
44
|
+
end
|
45
|
+
|
46
|
+
def https_remote?(remote)
|
47
|
+
remote.scan(/(https:\/\/)/).any?
|
48
|
+
end
|
49
|
+
|
50
|
+
def remote_project(remote)
|
51
|
+
if https_remote?(remote)
|
52
|
+
remote.scan(/https:\/\/[\S]+\/([\S]*).git/).first.first
|
53
|
+
elsif ssh_remote?(remote)
|
54
|
+
remote.scan(/\/([\S]*).git/).first.first
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def remote_source(remote)
|
59
|
+
if https_remote?(remote)
|
60
|
+
remote.scan(/https:\/\/([a-zA-z.]+)\//).first.first
|
61
|
+
elsif ssh_remote?(remote)
|
62
|
+
remote.scan(/git@([a-zA-z.]+):/).first.first
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def github_repo?
|
67
|
+
remotes.select { |remote| remote.include?('github') }.any?
|
68
|
+
end
|
69
|
+
|
70
|
+
def gitlab_project?
|
71
|
+
remotes.select { |remote| remote.include?('gitlab') }.any?
|
72
|
+
end
|
73
|
+
|
74
|
+
def project_name
|
11
75
|
# Get the repo/project name by looking in the remote URLs for the full name
|
12
76
|
`git remote -v`.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
|
13
77
|
end
|
@@ -17,24 +81,8 @@ module GitHelper
|
|
17
81
|
`git branch`.scan(/\*\s([\S]*)/).first.first
|
18
82
|
end
|
19
83
|
|
20
|
-
def default_branch
|
21
|
-
|
22
|
-
external_client.repository(project_name).default_branch
|
23
|
-
elsif client_type == :gitlab # GitLab project
|
24
|
-
page_number = 1
|
25
|
-
counter = 1
|
26
|
-
branches = []
|
27
|
-
|
28
|
-
while counter > 0
|
29
|
-
break if default_branch = branches.select { |branch| branch.default }.first
|
30
|
-
page_branches = external_client.branches(project_name, page: page_number, per_page: 100)
|
31
|
-
branches = page_branches
|
32
|
-
counter = page_branches.count
|
33
|
-
page_number += 1
|
34
|
-
end
|
35
|
-
|
36
|
-
default_branch.name
|
37
|
-
end
|
84
|
+
def default_branch
|
85
|
+
`git symbolic-ref refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@" | tr -d "\n"`
|
38
86
|
end
|
39
87
|
|
40
88
|
def template_options(template_identifiers)
|
@@ -1,17 +1,24 @@
|
|
1
|
-
require_relative './gitlab_client.rb'
|
2
|
-
require_relative './highline_cli.rb'
|
3
|
-
require_relative './local_code.rb'
|
4
|
-
|
5
1
|
module GitHelper
|
6
2
|
class GitLabMergeRequest
|
7
|
-
|
3
|
+
attr_accessor :local_project, :local_branch, :local_code, :cli, :base_branch, :new_mr_title
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@local_project = 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_mr_title = options[:new_title]
|
15
|
+
|
8
16
|
begin
|
9
|
-
# Ask these questions right away
|
10
|
-
base_branch
|
11
|
-
new_mr_title
|
12
17
|
options = {
|
13
18
|
source_branch: local_branch,
|
14
19
|
target_branch: base_branch,
|
20
|
+
squash: squash_merge_request,
|
21
|
+
remove_source_branch: remove_source_branch,
|
15
22
|
description: new_mr_body
|
16
23
|
}
|
17
24
|
|
@@ -34,16 +41,27 @@ module GitHelper
|
|
34
41
|
|
35
42
|
def merge
|
36
43
|
begin
|
37
|
-
# Ask these questions right away
|
38
44
|
mr_id
|
39
|
-
options = {
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
options = {
|
46
|
+
should_remove_source_branch: existing_mr.should_remove_source_branch || existing_mr.force_remove_source_branch,
|
47
|
+
squash: existing_mr.squash,
|
48
|
+
squash_commit_message: existing_mr.title
|
49
|
+
}
|
43
50
|
|
44
51
|
puts "Merging merge request: #{mr_id}"
|
45
52
|
merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
|
46
|
-
|
53
|
+
|
54
|
+
if merge.merge_commit_sha.nil?
|
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
|
47
65
|
rescue Gitlab::Error::MethodNotAllowed => e
|
48
66
|
puts 'Could not merge merge request:'
|
49
67
|
puts ' The merge request is not mergeable'
|
@@ -56,31 +74,36 @@ module GitHelper
|
|
56
74
|
end
|
57
75
|
end
|
58
76
|
|
59
|
-
private def
|
60
|
-
@
|
77
|
+
private def new_mr_body
|
78
|
+
@new_mr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
|
61
79
|
end
|
62
80
|
|
63
|
-
private def
|
64
|
-
@
|
65
|
-
|
81
|
+
private def template_name_to_apply
|
82
|
+
return @template_name_to_apply if @template_name_to_apply
|
83
|
+
@template_name_to_apply = nil
|
66
84
|
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
70
94
|
|
71
|
-
|
72
|
-
@default_branch ||= local_code.default_branch(local_project, gitlab_client, :gitlab)
|
95
|
+
@template_name_to_apply
|
73
96
|
end
|
74
97
|
|
75
98
|
private def mr_template_options
|
76
99
|
@mr_template_options ||= local_code.template_options({
|
77
|
-
nested_directory_name:
|
78
|
-
non_nested_file_name:
|
100
|
+
nested_directory_name: 'merge_request_templates',
|
101
|
+
non_nested_file_name: 'merge_request_template'
|
79
102
|
})
|
80
103
|
end
|
81
104
|
|
82
105
|
private def mr_id
|
83
|
-
@mr_id ||= cli.
|
106
|
+
@mr_id ||= cli.code_request_id('Merge')
|
84
107
|
end
|
85
108
|
|
86
109
|
private def squash_merge_request
|
@@ -88,60 +111,19 @@ module GitHelper
|
|
88
111
|
end
|
89
112
|
|
90
113
|
private def remove_source_branch
|
91
|
-
@remove_source_branch ||= cli.remove_source_branch?
|
92
|
-
end
|
93
|
-
|
94
|
-
private def new_mr_title
|
95
|
-
@new_mr_title ||= if cli.accept_autogenerated_title?(autogenerated_title)
|
96
|
-
autogenerated_title
|
97
|
-
else
|
98
|
-
cli.title
|
99
|
-
end
|
114
|
+
@remove_source_branch ||= existing_project.remove_source_branch_after_merge || cli.remove_source_branch?
|
100
115
|
end
|
101
116
|
|
102
|
-
private def
|
103
|
-
@
|
104
|
-
default_branch
|
105
|
-
else
|
106
|
-
cli.base_branch
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
private def new_mr_body
|
111
|
-
@new_mr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
|
112
|
-
end
|
113
|
-
|
114
|
-
private def template_name_to_apply
|
115
|
-
return @template_name_to_apply if @template_name_to_apply
|
116
|
-
@template_name_to_apply = nil
|
117
|
-
|
118
|
-
unless mr_template_options.empty?
|
119
|
-
if mr_template_options.count == 1
|
120
|
-
apply_single_template = cli.apply_template?(mr_template_options.first)
|
121
|
-
@template_name_to_apply = mr_template_options.first if apply_single_template
|
122
|
-
else
|
123
|
-
response = cli.template_to_apply(mr_template_options, 'merge')
|
124
|
-
@template_name_to_apply = response unless response == "None"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
@template_name_to_apply
|
117
|
+
private def existing_project
|
118
|
+
@existing_project ||= gitlab_client.project(local_project)
|
129
119
|
end
|
130
120
|
|
131
|
-
private def
|
132
|
-
@
|
121
|
+
private def existing_mr
|
122
|
+
@existing_mr ||= gitlab_client.merge_request(local_project, mr_id)
|
133
123
|
end
|
134
124
|
|
135
125
|
private def gitlab_client
|
136
126
|
@gitlab_client ||= GitHelper::GitLabClient.new.client
|
137
127
|
end
|
138
|
-
|
139
|
-
private def cli
|
140
|
-
@cli ||= GitHelper::HighlineCli.new
|
141
|
-
end
|
142
|
-
|
143
|
-
private def local_code
|
144
|
-
@local_code ||= GitHelper::LocalCode.new
|
145
|
-
end
|
146
128
|
end
|
147
129
|
end
|