git_helper 1.1.0 → 2.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.
@@ -1,7 +1,9 @@
1
+ require_relative './local_code.rb'
2
+
1
3
  module GitHelper
2
4
  class EmptyCommit
3
5
  def execute
4
- system("git commit --allow-empty -m \"Empty commit\"")
6
+ GitHelper::LocalCode.new.empty_commit
5
7
  end
6
8
  end
7
9
  end
@@ -0,0 +1,9 @@
1
+ require_relative './local_code.rb'
2
+
3
+ module GitHelper
4
+ class ForgetLocalCommits
5
+ def execute
6
+ GitHelper::LocalCode.new.forget_local_commits
7
+ end
8
+ end
9
+ end
@@ -2,24 +2,90 @@ require 'highline'
2
2
 
3
3
  module GitHelper
4
4
  class HighlineCli
5
- def ask(prompt)
6
- cli.ask(prompt) do |conf|
5
+ def new_branch_name
6
+ ask('New branch name?')
7
+ end
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
+
18
+ def title
19
+ ask('Title?')
20
+ end
21
+
22
+ def base_branch
23
+ ask('Base branch?')
24
+ end
25
+
26
+ def code_request_id(request_type)
27
+ ask("#{request_type} Request ID?")
28
+ end
29
+
30
+ def accept_autogenerated_title?(autogenerated_title)
31
+ return false unless autogenerated_title
32
+ answer = ask("Accept the autogenerated code request title '#{autogenerated_title}'? (y/n)")
33
+ answer.empty? ? true : !!(answer =~ /^y/i)
34
+ end
35
+
36
+ def base_branch_default?(default_branch)
37
+ answer = ask("Is '#{default_branch}' the correct base branch for your new code request? (y/n)")
38
+ answer.empty? ? true : !!(answer =~ /^y/i)
39
+ end
40
+
41
+ def squash_merge_request?
42
+ answer = ask('Squash merge request? (y/n)')
43
+ answer.empty? ? true : !!(answer =~ /^y/i)
44
+ end
45
+
46
+ def remove_source_branch?
47
+ answer = ask('Remove source branch after merging? (y/n)')
48
+ answer.empty? ? true : !!(answer =~ /^y/i)
49
+ end
50
+
51
+ def merge_method(merge_options)
52
+ index = ask_options("Merge method?", merge_options)
53
+ merge_options[index]
54
+ end
55
+
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)
59
+ end
60
+
61
+ def template_to_apply(template_options, request_type)
62
+ complete_options = template_options << 'None'
63
+ index = ask_options("Which #{request_type} request template should be applied?", complete_options)
64
+ complete_options[index]
65
+ end
66
+
67
+ #######################
68
+ ### GENERAL METHODS ###
69
+ #######################
70
+
71
+ private def ask(prompt)
72
+ highline_client.ask(prompt) do |conf|
7
73
  conf.readline = true
8
74
  end.to_s
9
75
  end
10
76
 
11
- def ask_options(prompt, choices)
77
+ private def ask_options(prompt, choices)
12
78
  choices_as_string_options = ''
13
79
  choices.each { |choice| choices_as_string_options << "#{choices.index(choice) + 1}. #{choice}\n" }
14
80
  compiled_prompt = "#{prompt}\n#{choices_as_string_options.strip}"
15
81
 
16
- cli.ask(compiled_prompt) do |conf|
82
+ highline_client.ask(compiled_prompt) do |conf|
17
83
  conf.readline = true
18
84
  end.to_i - 1
19
85
  end
20
86
 
21
- private def cli
22
- @cli ||= HighLine.new
87
+ private def highline_client
88
+ @highline_client ||= HighLine.new
23
89
  end
24
90
  end
25
91
  end
@@ -0,0 +1,124 @@
1
+ module GitHelper
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
+
23
+ def new_branch(branch_name)
24
+ system("git pull")
25
+ system("git branch --no-track #{branch_name}")
26
+ system("git checkout #{branch_name}")
27
+ system("git push --set-upstream origin #{branch_name}")
28
+ end
29
+
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
75
+ # Get the repo/project name by looking in the remote URLs for the full name
76
+ `git remote -v`.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
77
+ end
78
+
79
+ def branch
80
+ # Get the current branch by looking in the list of branches for the *
81
+ `git branch`.scan(/\*\s([\S]*)/).first.first
82
+ end
83
+
84
+ def default_branch
85
+ `git symbolic-ref refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@" | tr -d "\n"`
86
+ end
87
+
88
+ def template_options(template_identifiers)
89
+ nested_templates = Dir.glob(
90
+ File.join("**/#{template_identifiers[:nested_directory_name]}", "*.md"),
91
+ File::FNM_DOTMATCH | File::FNM_CASEFOLD
92
+ )
93
+ non_nested_templates = Dir.glob(
94
+ File.join("**", "#{template_identifiers[:non_nested_file_name]}.md"),
95
+ File::FNM_DOTMATCH | File::FNM_CASEFOLD
96
+ )
97
+ nested_templates.concat(non_nested_templates).uniq
98
+ end
99
+
100
+ def read_template(file_name)
101
+ File.open(file_name).read
102
+ end
103
+
104
+ def generate_title(local_branch)
105
+ branch_arr = local_branch.split(local_branch.include?('_') ? '_' : '-')
106
+
107
+ return if branch_arr.empty?
108
+
109
+ if branch_arr.length == 1
110
+ branch_arr.first.capitalize
111
+ elsif branch_arr[0].scan(/([\w]+)/).any? && branch_arr[1].scan(/([\d]+)/).any? # branch includes jira_123 at beginning
112
+ issue = "#{branch_arr[0].upcase}-#{branch_arr[1]}"
113
+ description = branch_arr[2..-1].join(' ')
114
+ "#{issue} #{description.capitalize}"
115
+ elsif branch_arr[0].scan(/([\w]+-[\d]+)/).any? # branch includes string jira-123 at beginning
116
+ issue = branch_arr[0].upcase
117
+ description = branch_arr[1..-1].join(' ')
118
+ "#{issue} #{description.capitalize}"
119
+ else # plain words
120
+ branch_arr[0..-1].join(' ').capitalize
121
+ end
122
+ end
123
+ end
124
+ end
@@ -1,21 +1,33 @@
1
1
  require_relative './gitlab_client.rb'
2
2
  require_relative './highline_cli.rb'
3
+ require_relative './local_code.rb'
3
4
 
4
5
  module GitHelper
5
6
  class GitLabMergeRequest
6
- def create
7
+ attr_accessor :local_project, :local_branch, :local_code, :cli, :base_branch, :new_mr_title
8
+
9
+ def initialize(options)
10
+ @local_project = options[:local_project]
11
+ @local_branch = options[:local_branch]
12
+ @local_code = options[:local_code]
13
+ @cli = options[:cli]
14
+ end
15
+
16
+ def create(options)
17
+ @base_branch = options[:base_branch]
18
+ @new_mr_title = options[:new_title]
19
+
7
20
  begin
8
- # Ask these questions right away
9
- base_branch
10
- new_mr_title
11
21
  options = {
12
22
  source_branch: local_branch,
13
23
  target_branch: base_branch,
24
+ squash: squash_merge_request,
25
+ remove_source_branch: remove_source_branch,
14
26
  description: new_mr_body
15
27
  }
16
28
 
17
29
  puts "Creating merge request: #{new_mr_title}"
18
- mr = gitlab_client.create_merge_request(local_repo, new_mr_title, options)
30
+ mr = gitlab_client.create_merge_request(local_project, new_mr_title, options)
19
31
 
20
32
  if mr.diff_refs.base_sha == mr.diff_refs.head_sha
21
33
  puts "Merge request was created, but no commits have been pushed to GitLab: #{mr.web_url}"
@@ -33,16 +45,27 @@ module GitHelper
33
45
 
34
46
  def merge
35
47
  begin
36
- # Ask these questions right away
37
48
  mr_id
38
- options = {}
39
- options[:should_remove_source_branch] = remove_source_branch?
40
- options[:squash] = squash_merge_request?
41
- options[:squash_commit_message] = existing_mr_title
49
+ options = {
50
+ should_remove_source_branch: existing_mr.should_remove_source_branch || existing_mr.force_remove_source_branch,
51
+ squash: existing_mr.squash,
52
+ squash_commit_message: existing_mr.title
53
+ }
42
54
 
43
55
  puts "Merging merge request: #{mr_id}"
44
- merge = gitlab_client.accept_merge_request(local_repo, mr_id, options)
45
- puts "Merge request successfully merged: #{merge.merge_commit_sha}"
56
+ merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
57
+
58
+ if merge.merge_commit_sha.nil?
59
+ options[:squash] = true
60
+ merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
61
+ end
62
+
63
+ if merge.merge_commit_sha.nil?
64
+ puts 'Could not merge merge request:'
65
+ puts " #{merge.merge_error}"
66
+ else
67
+ puts "Merge request successfully merged: #{merge.merge_commit_sha}"
68
+ end
46
69
  rescue Gitlab::Error::MethodNotAllowed => e
47
70
  puts 'Could not merge merge request:'
48
71
  puts ' The merge request is not mergeable'
@@ -55,100 +78,56 @@ module GitHelper
55
78
  end
56
79
  end
57
80
 
58
- private def local_repo
59
- # Get the repository by looking in the remote URLs for the full repository name
60
- remotes = `git remote -v`
61
- return remotes.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
62
- end
63
-
64
- private def local_branch
65
- # Get the current branch by looking in the list of branches for the *
66
- branches = `git branch`
67
- return branches.scan(/\*\s([\S]*)/).first.first
68
- end
69
-
70
- private def read_template
71
- if mr_template_options.count == 1
72
- apply_template?(mr_template_options.first) ? File.open(mr_template_options.first).read : ''
73
- else
74
- template_file_name_to_apply = template_to_apply
75
- template_file_name_to_apply == "None" ? '' : File.open(template_file_name_to_apply).read
76
- end
77
- end
78
-
79
- private def mr_id
80
- @mr_id ||= cli.ask('Merge Request ID?')
81
- end
82
-
83
- private def existing_mr_title
84
- @existing_mr_title ||= gitlab_client.merge_request(local_repo, mr_id).title
85
- end
86
-
87
- private def new_mr_title
88
- @new_mr_title ||= accept_autogenerated_title? ? autogenerated_title : cli.ask('Title?')
89
- end
90
-
91
81
  private def new_mr_body
92
- @new_mr_body ||= mr_template_options.empty? ? '' : read_template
82
+ @new_mr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
93
83
  end
94
84
 
95
- private def base_branch
96
- @base_branch ||= base_branch_default? ? default_branch : cli.ask('Base branch?')
97
- end
85
+ private def template_name_to_apply
86
+ return @template_name_to_apply if @template_name_to_apply
87
+ @template_name_to_apply = nil
98
88
 
99
- private def autogenerated_title
100
- @autogenerated_title ||= local_branch.split('_')[0..-1].join(' ').capitalize
101
- end
102
-
103
- private def default_branch
104
- @default_branch ||= gitlab_client.branches(local_repo).select { |branch| branch.default }.first.name
105
- end
89
+ unless mr_template_options.empty?
90
+ if mr_template_options.count == 1
91
+ apply_single_template = cli.apply_template?(mr_template_options.first, 'merge')
92
+ @template_name_to_apply = mr_template_options.first if apply_single_template
93
+ else
94
+ response = cli.template_to_apply(mr_template_options, 'merge')
95
+ @template_name_to_apply = response unless response == 'None'
96
+ end
97
+ end
106
98
 
107
- private def template_to_apply
108
- return @template_to_apply if @template_to_apply
109
- complete_options = mr_template_options << 'None'
110
- index = cli.ask_options("Which merge request template should be applied?", complete_options)
111
- @template_to_apply = complete_options[index]
99
+ @template_name_to_apply
112
100
  end
113
101
 
114
102
  private def mr_template_options
115
- return @mr_template_options if @mr_template_options
116
- nested_templates = Dir.glob(File.join("**/merge_request_templates", "*.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
117
- non_nested_templates = Dir.glob(File.join("**", "merge_request_template.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
118
- @mr_template_options = nested_templates.concat(non_nested_templates)
103
+ @mr_template_options ||= local_code.template_options({
104
+ nested_directory_name: 'merge_request_templates',
105
+ non_nested_file_name: 'merge_request_template'
106
+ })
119
107
  end
120
108
 
121
- private def base_branch_default?
122
- answer = cli.ask("Is '#{default_branch}' the correct base branch for your new merge request? (y/n)")
123
- !!(answer =~ /^y/i)
109
+ private def mr_id
110
+ @mr_id ||= cli.code_request_id('Merge')
124
111
  end
125
112
 
126
- private def accept_autogenerated_title?
127
- answer = cli.ask("Accept the autogenerated merge request title '#{autogenerated_title}'? (y/n)")
128
- !!(answer =~ /^y/i)
113
+ private def squash_merge_request
114
+ @squash_merge_request ||= cli.squash_merge_request?
129
115
  end
130
116
 
131
- private def squash_merge_request?
132
- answer = cli.ask('Squash merge request? (y/n)')
133
- !!(answer =~ /^y/i)
117
+ private def remove_source_branch
118
+ @remove_source_branch ||= existing_project.remove_source_branch_after_merge || cli.remove_source_branch?
134
119
  end
135
120
 
136
- private def remove_source_branch?
137
- answer = cli.ask('Remove source branch after merging? (y/n)')
138
- !!(answer =~ /^y/i)
121
+ private def existing_project
122
+ @existing_project ||= gitlab_client.project(local_project)
139
123
  end
140
124
 
141
- private def apply_template?(template_file_name)
142
- answer = cli.ask("Apply the merge request template from #{template_file_name}? (y/n)")
143
- !!(answer =~ /^y/i)
125
+ private def existing_mr
126
+ @existing_mr ||= gitlab_client.merge_request(local_project, mr_id)
144
127
  end
145
128
 
146
129
  private def gitlab_client
147
130
  @gitlab_client ||= GitHelper::GitLabClient.new.client
148
131
  end
149
-
150
- private def cli
151
- @cli ||= GitHelper::HighlineCli.new
152
- end
153
132
  end
154
133
  end
@@ -1,18 +1,12 @@
1
1
  require_relative './highline_cli.rb'
2
+ require_relative './local_code.rb'
2
3
 
3
4
  module GitHelper
4
5
  class NewBranch
5
6
  def execute(new_branch_name = nil)
6
- branch_name = new_branch_name || cli.ask('New branch name?')
7
+ branch_name = new_branch_name || GitHelper::HighlineCli.new.new_branch_name
7
8
  puts "Attempting to create a new branch: #{branch_name}"
8
- system("git pull")
9
- system("git branch --no-track #{branch_name}")
10
- system("git checkout #{branch_name}")
11
- system("git push --set-upstream origin #{branch_name}")
12
- end
13
-
14
- private def cli
15
- @cli ||= GitHelper::HighlineCli.new
9
+ GitHelper::LocalCode.new.new_branch(branch_name)
16
10
  end
17
11
  end
18
12
  end