git_helper 1.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +17 -13
  3. data/README.md +37 -44
  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/git_config_reader.rb +1 -1
  14. data/lib/git_helper/gitlab_client.rb +0 -1
  15. data/lib/git_helper/highline_cli.rb +21 -17
  16. data/lib/git_helper/local_code.rb +67 -19
  17. data/lib/git_helper/merge_request.rb +55 -73
  18. data/lib/git_helper/new_branch.rb +2 -13
  19. data/lib/git_helper/octokit_client.rb +0 -1
  20. data/lib/git_helper/pull_request.rb +44 -67
  21. data/lib/git_helper/version.rb +1 -1
  22. data/plugins.zip +0 -0
  23. data/spec/git_helper/change_remote_spec.rb +173 -0
  24. data/spec/git_helper/checkout_default_spec.rb +19 -0
  25. data/spec/git_helper/clean_branches_spec.rb +19 -0
  26. data/spec/git_helper/code_request_spec.rb +259 -0
  27. data/spec/git_helper/empty_commit_spec.rb +19 -0
  28. data/spec/git_helper/forget_local_commits_spec.rb +19 -0
  29. data/spec/git_helper/git_config_reader_spec.rb +60 -0
  30. data/spec/git_helper/gitlab_client_spec.rb +26 -0
  31. data/spec/git_helper/highline_cli_spec.rb +215 -0
  32. data/spec/git_helper/local_code_spec.rb +231 -0
  33. data/spec/git_helper/merge_request_spec.rb +234 -0
  34. data/spec/git_helper/new_branch_spec.rb +44 -0
  35. data/spec/git_helper/octokit_client_spec.rb +26 -0
  36. data/spec/git_helper/pull_request_spec.rb +246 -0
  37. data/spec/spec_helper.rb +0 -7
  38. metadata +41 -24
@@ -1,7 +1,7 @@
1
1
  module GitHelper
2
2
  class CheckoutDefault
3
3
  def execute
4
- system("git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed \"s@^refs/remotes/origin/@@\")")
4
+ GitHelper::LocalCode.new.checkout_default
5
5
  end
6
6
  end
7
7
  end
@@ -1,10 +1,7 @@
1
1
  module GitHelper
2
2
  class CleanBranches
3
3
  def execute
4
- system("git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed \"s@^refs/remotes/origin/@@\")")
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
@@ -1,7 +1,7 @@
1
1
  module GitHelper
2
2
  class EmptyCommit
3
3
  def execute
4
- system("git commit --allow-empty -m \"Empty commit\"")
4
+ GitHelper::LocalCode.new.empty_commit
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,7 @@
1
+ module GitHelper
2
+ class ForgetLocalCommits
3
+ def execute
4
+ GitHelper::LocalCode.new.forget_local_commits
5
+ end
6
+ end
7
+ end
@@ -19,7 +19,7 @@ module GitHelper
19
19
  end
20
20
 
21
21
  private def git_config_file
22
- '.git_config.yml'
22
+ '.git_helper/config.yml'
23
23
  end
24
24
  end
25
25
  end
@@ -1,5 +1,4 @@
1
1
  require 'gitlab'
2
- require_relative './git_config_reader.rb'
3
2
 
4
3
  module GitHelper
5
4
  class GitLabClient
@@ -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 merge_request_id
18
- ask('Merge Request ID?')
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 merge request title '#{autogenerated_title}'? (y/n)")
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 merge request? (y/n)")
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 merge request template from #{template_file_name}? (y/n)")
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 name
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(project_name, external_client, client_type)
21
- if client_type == :octokit # GitHub repository
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
- def create
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
- options[:should_remove_source_branch] = remove_source_branch
41
- options[:squash] = squash_merge_request
42
- options[:squash_commit_message] = existing_mr_title
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
- puts "Merge request successfully merged: #{merge.merge_commit_sha}"
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 local_project
60
- @local_project ||= local_code.name
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 local_branch
64
- @local_branch ||= local_code.branch
65
- end
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
- private def autogenerated_title
68
- @autogenerated_title ||= local_code.generate_title(local_branch)
69
- end
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
- private def default_branch
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: "merge_request_templates",
78
- non_nested_file_name: "merge_request_template"
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.merge_request_id
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 base_branch
103
- @base_branch ||= if cli.base_branch_default?(default_branch)
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 existing_mr_title
132
- @existing_mr_title ||= gitlab_client.merge_request(local_project, mr_id).title
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