git_helper 1.3.1 → 3.0.1

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