git_helper 1.3.1 → 3.0.1

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 +42 -47
  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 +20 -15
  16. data/lib/git_helper/local_code.rb +75 -23
  17. data/lib/git_helper/merge_request.rb +45 -74
  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 +34 -69
  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 +232 -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,33 +23,29 @@ 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
51
  def merge_method(merge_options)
@@ -48,9 +53,9 @@ module GitHelper
48
53
  merge_options[index]
49
54
  end
50
55
 
51
- def apply_template?(template_file_name)
52
- answer = ask("Apply the merge request template from #{template_file_name}? (y/n)")
53
- !!(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)
54
59
  end
55
60
 
56
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,36 +81,24 @@ 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
- def template_options(template_identifiers)
88
+ def template_options(identifiers)
41
89
  nested_templates = Dir.glob(
42
- File.join("**/#{template_identifiers[:nested_directory_name]}", "*.md"),
90
+ File.join("#{identifiers[:template_directory]}/#{identifiers[:nested_directory_name]}", '*.md'),
43
91
  File::FNM_DOTMATCH | File::FNM_CASEFOLD
44
92
  )
45
93
  non_nested_templates = Dir.glob(
46
- File.join("**", "#{template_identifiers[:non_nested_file_name]}.md"),
94
+ File.join(identifiers[:template_directory], "#{identifiers[:non_nested_file_name]}.md"),
95
+ File::FNM_DOTMATCH | File::FNM_CASEFOLD
96
+ )
97
+ root_templates = Dir.glob(
98
+ File.join('.', "#{identifiers[:non_nested_file_name]}.md"),
47
99
  File::FNM_DOTMATCH | File::FNM_CASEFOLD
48
100
  )
49
- nested_templates.concat(non_nested_templates).uniq
101
+ nested_templates.concat(non_nested_templates).concat(root_templates).uniq
50
102
  end
51
103
 
52
104
  def read_template(file_name)
@@ -1,14 +1,19 @@
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,
@@ -36,12 +41,12 @@ module GitHelper
36
41
 
37
42
  def merge
38
43
  begin
39
- # Ask these questions right away
40
44
  mr_id
41
- options = {}
42
- options[:should_remove_source_branch] = existing_mr.should_remove_source_branch || existing_mr.force_remove_source_branch
43
- options[:squash] = existing_mr.squash
44
- 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
+ }
45
50
 
46
51
  puts "Merging merge request: #{mr_id}"
47
52
  merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
@@ -51,7 +56,12 @@ module GitHelper
51
56
  merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
52
57
  end
53
58
 
54
- puts "Merge request successfully merged: #{merge.merge_commit_sha}"
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
55
65
  rescue Gitlab::Error::MethodNotAllowed => e
56
66
  puts 'Could not merge merge request:'
57
67
  puts ' The merge request is not mergeable'
@@ -64,31 +74,37 @@ module GitHelper
64
74
  end
65
75
  end
66
76
 
67
- private def local_project
68
- @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) : ''
69
79
  end
70
80
 
71
- private def local_branch
72
- @local_branch ||= local_code.branch
73
- 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
74
84
 
75
- private def autogenerated_title
76
- @autogenerated_title ||= local_code.generate_title(local_branch)
77
- 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
78
94
 
79
- private def default_branch
80
- @default_branch ||= local_code.default_branch(local_project, gitlab_client, :gitlab)
95
+ @template_name_to_apply
81
96
  end
82
97
 
83
98
  private def mr_template_options
84
99
  @mr_template_options ||= local_code.template_options({
85
- nested_directory_name: "merge_request_templates",
86
- non_nested_file_name: "merge_request_template"
100
+ template_directory: '.gitlab',
101
+ nested_directory_name: 'merge_request_templates',
102
+ non_nested_file_name: 'merge_request_template'
87
103
  })
88
104
  end
89
105
 
90
106
  private def mr_id
91
- @mr_id ||= cli.merge_request_id
107
+ @mr_id ||= cli.code_request_id('Merge')
92
108
  end
93
109
 
94
110
  private def squash_merge_request
@@ -99,61 +115,16 @@ module GitHelper
99
115
  @remove_source_branch ||= existing_project.remove_source_branch_after_merge || cli.remove_source_branch?
100
116
  end
101
117
 
102
- private def new_mr_title
103
- @new_mr_title ||= if cli.accept_autogenerated_title?(autogenerated_title)
104
- autogenerated_title
105
- else
106
- cli.title
107
- end
108
- end
109
-
110
- private def base_branch
111
- @base_branch ||= if cli.base_branch_default?(default_branch)
112
- default_branch
113
- else
114
- cli.base_branch
115
- end
116
- end
117
-
118
- private def new_mr_body
119
- @new_mr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
120
- end
121
-
122
- private def template_name_to_apply
123
- return @template_name_to_apply if @template_name_to_apply
124
- @template_name_to_apply = nil
125
-
126
- unless mr_template_options.empty?
127
- if mr_template_options.count == 1
128
- apply_single_template = cli.apply_template?(mr_template_options.first)
129
- @template_name_to_apply = mr_template_options.first if apply_single_template
130
- else
131
- response = cli.template_to_apply(mr_template_options, 'merge')
132
- @template_name_to_apply = response unless response == "None"
133
- end
134
- end
135
-
136
- @template_name_to_apply
118
+ private def existing_project
119
+ @existing_project ||= gitlab_client.project(local_project)
137
120
  end
138
121
 
139
122
  private def existing_mr
140
123
  @existing_mr ||= gitlab_client.merge_request(local_project, mr_id)
141
124
  end
142
125
 
143
- private def existing_project
144
- @existing_project ||= gitlab_client.project(local_project)
145
- end
146
-
147
126
  private def gitlab_client
148
127
  @gitlab_client ||= GitHelper::GitLabClient.new.client
149
128
  end
150
-
151
- private def cli
152
- @cli ||= GitHelper::HighlineCli.new
153
- end
154
-
155
- private def local_code
156
- @local_code ||= GitHelper::LocalCode.new
157
- end
158
129
  end
159
130
  end