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