Skip to content

Commit f9c8ed7

Browse files
committed
fix: fix Rubocop Metrics/MethodLength offense
1 parent 52d80ac commit f9c8ed7

File tree

8 files changed

+683
-423
lines changed

8 files changed

+683
-423
lines changed

.rubocop.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ inherit_from: .rubocop_todo.yml
33
inherit_gem:
44
main_branch_shared_rubocop_config: config/rubocop.yml
55

6+
# Don't care so much about length of methods in tests
7+
Metrics/MethodLength:
8+
Exclude:
9+
- "tests/test_helper.rb"
10+
- "tests/units/**/*"
11+
612
# Allow test data to have long lines
713
Layout/LineLength:
814
Exclude:

.rubocop_todo.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
# Note that changes in the inspected code, or installation of new
77
# versions of RuboCop, may require this file to be generated again.
88

9+
# # Offense count: 111
10+
# # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
11+
# Metrics/MethodLength:
12+
# Max: 51
13+
914
# Offense count: 68
1015
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
1116
Metrics/AbcSize:
@@ -21,11 +26,6 @@ Metrics/ClassLength:
2126
Metrics/CyclomaticComplexity:
2227
Max: 21
2328

24-
# Offense count: 111
25-
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
26-
Metrics/MethodLength:
27-
Max: 51
28-
2929
# Offense count: 12
3030
# Configuration parameters: AllowedMethods, AllowedPatterns.
3131
Metrics/PerceivedComplexity:

lib/git/base.rb

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,29 @@ def self.config
3939
end
4040

4141
def self.binary_version(binary_path)
42-
result = nil
43-
status = nil
44-
45-
begin
46-
result, status = Open3.capture2e(binary_path, '-c', 'core.quotePath=true', '-c', 'color.ui=false', 'version')
47-
result = result.chomp
48-
rescue Errno::ENOENT
49-
raise "Failed to get git version: #{binary_path} not found"
50-
end
42+
result, status = execute_git_version(binary_path)
5143

5244
raise "Failed to get git version: #{status}\n#{result}" unless status.success?
5345

54-
version = result[/\d+(\.\d+)+/]
55-
version_parts = version.split('.').collect(&:to_i)
46+
parse_version_string(result)
47+
end
48+
49+
private_class_method def self.execute_git_version(binary_path)
50+
Open3.capture2e(
51+
binary_path,
52+
'-c', 'core.quotePath=true',
53+
'-c', 'color.ui=false',
54+
'version'
55+
)
56+
rescue Errno::ENOENT
57+
raise "Failed to get git version: #{binary_path} not found"
58+
end
59+
60+
private_class_method def self.parse_version_string(raw_string)
61+
version_match = raw_string.match(/\d+(\.\d+)+/)
62+
return [0, 0, 0] unless version_match
63+
64+
version_parts = version_match[0].split('.').map(&:to_i)
5665
version_parts.fill(0, version_parts.length...3)
5766
end
5867

@@ -85,24 +94,28 @@ def self.init(directory = '.', options = {})
8594
end
8695

8796
def self.root_of_worktree(working_dir)
88-
result = working_dir
89-
status = nil
90-
9197
raise ArgumentError, "'#{working_dir}' does not exist" unless Dir.exist?(working_dir)
9298

93-
begin
94-
result, status = Open3.capture2e(
95-
Git::Base.config.binary_path, '-c', 'core.quotePath=true', '-c',
96-
'color.ui=false', 'rev-parse', '--show-toplevel', chdir: File.expand_path(working_dir)
97-
)
98-
result = result.chomp
99-
rescue Errno::ENOENT
100-
raise ArgumentError, 'Failed to find the root of the worktree: git binary not found'
101-
end
99+
result, status = execute_rev_parse_toplevel(working_dir)
100+
process_rev_parse_result(result, status, working_dir)
101+
end
102+
103+
private_class_method def self.execute_rev_parse_toplevel(working_dir)
104+
Open3.capture2e(
105+
Git::Base.config.binary_path,
106+
'-c', 'core.quotePath=true',
107+
'-c', 'color.ui=false',
108+
'rev-parse', '--show-toplevel',
109+
chdir: File.expand_path(working_dir)
110+
)
111+
rescue Errno::ENOENT
112+
raise ArgumentError, 'Failed to find the root of the worktree: git binary not found'
113+
end
102114

115+
private_class_method def self.process_rev_parse_result(result, status, working_dir)
103116
raise ArgumentError, "'#{working_dir}' is not in a git working tree" unless status.success?
104117

105-
result
118+
result.chomp
106119
end
107120

108121
# (see Git.open)
@@ -879,19 +892,58 @@ def diff_path_status(objectish = 'HEAD', obj2 = nil)
879892
# 2. the working directory if NOT working with a bare repository
880893
#
881894
private_class_method def self.normalize_repository(options, default:, bare: false)
882-
repository =
883-
if bare
884-
File.expand_path(options[:repository] || default || Dir.pwd)
885-
else
886-
File.expand_path(options[:repository] || '.git', options[:working_directory])
887-
end
895+
initial_path = initial_repository_path(options, default: default, bare: bare)
896+
final_path = resolve_gitdir_if_present(initial_path, options[:working_directory])
897+
options[:repository] = final_path
898+
end
888899

889-
if File.file?(repository)
890-
repository = File.expand_path(File.read(repository)[8..].strip,
891-
options[:working_directory])
900+
# Determines the initial, potential path to the repository directory
901+
#
902+
# This path is considered 'initial' because it is not guaranteed to be the
903+
# final repository location. For features like submodules or worktrees,
904+
# this path may point to a text file containing a `gitdir:` pointer to the
905+
# actual repository directory elsewhere. This initial path must be
906+
# subsequently resolved.
907+
#
908+
# @api private
909+
#
910+
# @param options [Hash] The options hash, checked for `[:repository]`.
911+
#
912+
# @param default [String] A fallback path if `options[:repository]` is not set.
913+
#
914+
# @param bare [Boolean] Whether the repository is bare, which changes path resolution.
915+
#
916+
# @return [String] The initial, absolute path to the `.git` directory or file.
917+
#
918+
private_class_method def self.initial_repository_path(options, default:, bare:)
919+
if bare
920+
File.expand_path(options[:repository] || default || Dir.pwd)
921+
else
922+
File.expand_path(options[:repository] || '.git', options[:working_directory])
892923
end
924+
end
925+
926+
# Resolves the path to the actual repository if it's a `gitdir:` pointer file.
927+
#
928+
# If `path` points to a file (common in submodules and worktrees), this
929+
# method reads the `gitdir:` path from it and returns the real repository
930+
# path. Otherwise, it returns the original path.
931+
#
932+
# @api private
933+
#
934+
# @param path [String] The initial path to the repository, which may be a pointer file.
935+
#
936+
# @param working_dir [String] The working directory, used as a base to resolve the path.
937+
#
938+
# @return [String] The final, resolved absolute path to the repository directory.
939+
#
940+
private_class_method def self.resolve_gitdir_if_present(path, working_dir)
941+
return path unless File.file?(path)
893942

894-
options[:repository] = repository
943+
# The file contains `gitdir: <path>`, so we read the file,
944+
# extract the path part, and expand it.
945+
gitdir_pointer = File.read(path).sub(/\Agitdir: /, '').strip
946+
File.expand_path(gitdir_pointer, working_dir)
895947
end
896948

897949
# Normalize options[:index]

lib/git/diff.rb

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -115,41 +115,78 @@ def process_full
115115
@full_diff_files = process_full_diff
116116
end
117117

118-
# CORRECTED: Pass the @path variable to the new objects
119118
def path_status_provider
120119
@path_status_provider ||= Git::DiffPathStatus.new(@base, @from, @to, @path)
121120
end
122121

123-
# CORRECTED: Pass the @path variable to the new objects
124122
def stats_provider
125123
@stats_provider ||= Git::DiffStats.new(@base, @from, @to, @path)
126124
end
127125

128126
def process_full_diff
129-
defaults = {
130-
mode: '', src: '', dst: '', type: 'modified'
131-
}
132-
final = {}
133-
current_file = nil
134-
patch.split("\n").each do |line|
135-
if (m = %r{\Adiff --git ("?)a/(.+?)\1 ("?)b/(.+?)\3\z}.match(line))
136-
current_file = Git::EscapedPath.new(m[2]).unescape
137-
final[current_file] = defaults.merge({ patch: line, path: current_file })
127+
FullDiffParser.new(@base, patch).parse
128+
end
129+
130+
# A private parser class to process the output of `git diff`
131+
# @api private
132+
class FullDiffParser
133+
def initialize(base, patch_text)
134+
@base = base
135+
@patch_text = patch_text
136+
@final_files = {}
137+
@current_file_data = nil
138+
@defaults = { mode: '', src: '', dst: '', type: 'modified', binary: false }
139+
end
140+
141+
def parse
142+
@patch_text.split("\n").each { |line| process_line(line) }
143+
@final_files.map { |filename, data| [filename, DiffFile.new(@base, data)] }
144+
end
145+
146+
private
147+
148+
def process_line(line)
149+
if (new_file_match = line.match(%r{\Adiff --git ("?)a/(.+?)\1 ("?)b/(.+?)\3\z}))
150+
start_new_file(new_file_match, line)
138151
else
139-
if (m = /^index ([0-9a-f]{4,40})\.\.([0-9a-f]{4,40})( ......)*/.match(line))
140-
final[current_file][:src] = m[1]
141-
final[current_file][:dst] = m[2]
142-
final[current_file][:mode] = m[3].strip if m[3]
143-
end
144-
if (m = /^([[:alpha:]]*?) file mode (......)/.match(line))
145-
final[current_file][:type] = m[1]
146-
final[current_file][:mode] = m[2]
147-
end
148-
final[current_file][:binary] = true if /^Binary files /.match(line)
149-
final[current_file][:patch] << "\n#{line}"
152+
append_to_current_file(line)
150153
end
151154
end
152-
final.map { |e| [e[0], DiffFile.new(@base, e[1])] }
155+
156+
def start_new_file(match, line)
157+
filename = Git::EscapedPath.new(match[2]).unescape
158+
@current_file_data = @defaults.merge({ patch: line, path: filename })
159+
@final_files[filename] = @current_file_data
160+
end
161+
162+
def append_to_current_file(line)
163+
return unless @current_file_data
164+
165+
parse_index_line(line)
166+
parse_file_mode_line(line)
167+
check_for_binary(line)
168+
169+
@current_file_data[:patch] << "\n#{line}"
170+
end
171+
172+
def parse_index_line(line)
173+
return unless (match = line.match(/^index ([0-9a-f]{4,40})\.\.([0-9a-f]{4,40})( ......)*/))
174+
175+
@current_file_data[:src] = match[1]
176+
@current_file_data[:dst] = match[2]
177+
@current_file_data[:mode] = match[3].strip if match[3]
178+
end
179+
180+
def parse_file_mode_line(line)
181+
return unless (match = line.match(/^([[:alpha:]]*?) file mode (......)/))
182+
183+
@current_file_data[:type] = match[1]
184+
@current_file_data[:mode] = match[2]
185+
end
186+
187+
def check_for_binary(line)
188+
@current_file_data[:binary] = true if line.match?(/^Binary files /)
189+
end
153190
end
154191
end
155192
end

0 commit comments

Comments
 (0)