git_helper 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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