@@ -39,20 +39,29 @@ def self.config
39
39
end
40
40
41
41
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 )
51
43
52
44
raise "Failed to get git version: #{ status } \n #{ result } " unless status . success?
53
45
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 )
56
65
version_parts . fill ( 0 , version_parts . length ...3 )
57
66
end
58
67
@@ -85,24 +94,28 @@ def self.init(directory = '.', options = {})
85
94
end
86
95
87
96
def self . root_of_worktree ( working_dir )
88
- result = working_dir
89
- status = nil
90
-
91
97
raise ArgumentError , "'#{ working_dir } ' does not exist" unless Dir . exist? ( working_dir )
92
98
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
102
114
115
+ private_class_method def self . process_rev_parse_result ( result , status , working_dir )
103
116
raise ArgumentError , "'#{ working_dir } ' is not in a git working tree" unless status . success?
104
117
105
- result
118
+ result . chomp
106
119
end
107
120
108
121
# (see Git.open)
@@ -879,19 +892,58 @@ def diff_path_status(objectish = 'HEAD', obj2 = nil)
879
892
# 2. the working directory if NOT working with a bare repository
880
893
#
881
894
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
888
899
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 ] )
892
923
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 )
893
942
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 ( /\A gitdir: / , '' ) . strip
946
+ File . expand_path ( gitdir_pointer , working_dir )
895
947
end
896
948
897
949
# Normalize options[:index]
0 commit comments