@@ -34,14 +34,14 @@ class Worktree
3434 #
3535 # @return [RubyGit::Worktree] the working tree whose root is at `path`
3636 #
37- def self . init ( worktree_path )
37+ def self . init ( worktree_path , normalize_path : true )
3838 raise RubyGit ::Error , "Path '#{ worktree_path } ' not valid." unless File . directory? ( worktree_path )
3939
4040 command = [ 'init' ]
4141 options = { chdir : worktree_path , out : StringIO . new , err : StringIO . new }
4242 RubyGit ::CommandLine . run ( *command , **options )
4343
44- new ( worktree_path )
44+ new ( worktree_path , normalize_path : )
4545 end
4646
4747 # Open an existing Git working tree that contains worktree_path
@@ -58,8 +58,8 @@ def self.init(worktree_path)
5858 #
5959 # @return [RubyGit::Worktree] the Git working tree that contains `worktree_path`
6060 #
61- def self . open ( worktree_path )
62- new ( worktree_path )
61+ def self . open ( worktree_path , normalize_path : true )
62+ new ( worktree_path , normalize_path : )
6363 end
6464
6565 # Copy the remote repository and checkout the default branch
@@ -97,23 +97,12 @@ def self.open(worktree_path)
9797 #
9898 # @return [RubyGit::Worktree] the Git working tree checked out from the cloned repository
9999 #
100- def self . clone ( repository_url , to_path : nil )
100+ def self . clone ( repository_url , to_path : nil , normalize_path : true )
101101 command = [ 'clone' , '--' , repository_url ]
102102 command << to_path if to_path
103103 options = { out : StringIO . new , err : StringIO . new }
104104 clone_output = RubyGit ::CommandLine . run ( *command , **options ) . stderr
105- new ( cloned_to ( clone_output ) )
106- end
107-
108- # Get path of the cloned worktree from `git clone` stderr output
109- #
110- # @param clone_output [String] the stderr output of the `git clone` command
111- #
112- # @return [String] the path of the cloned worktree
113- #
114- # @api private
115- def self . cloned_to ( clone_output )
116- clone_output . match ( /Cloning into ['"](.+)['"]\. \. \. / ) [ 1 ]
105+ new ( cloned_to ( clone_output ) , normalize_path :)
117106 end
118107
119108 # Show the working tree and index status
@@ -154,7 +143,7 @@ def status(*path_specs, untracked_files: :all, ignored: :no, ignore_submodules:
154143 command << '--' unless path_specs . empty?
155144 command . concat ( path_specs )
156145 options = { out : StringIO . new , err : StringIO . new }
157- status_output = run ( *command , **options ) . stdout
146+ status_output = run_with_context ( *command , **options ) . stdout
158147 RubyGit ::Status . parse ( status_output )
159148 end
160149
@@ -194,7 +183,7 @@ def add(*pathspecs, all: false, force: false, refresh: false, update: false) # r
194183
195184 options = { out : StringIO . new , err : StringIO . new }
196185
197- run ( *command , **options )
186+ run_with_context ( *command , **options )
198187 end
199188
200189 # Return the repository associated with the worktree
@@ -211,7 +200,7 @@ def repository
211200 options = { chdir : path , chomp : true , out : StringIO . new , err : StringIO . new }
212201 # rev-parse path might be relative to the worktree, thus the need to expand it
213202 git_dir = File . realpath ( RubyGit ::CommandLine . run ( *command , **options ) . stdout , path )
214- Repository . new ( git_dir )
203+ Repository . new ( git_dir , normalize_path : normalize_path? )
215204 end
216205 end
217206
@@ -220,28 +209,94 @@ def repository
220209 # Create a Worktree object
221210 #
222211 # @param worktree_path [String] a path anywhere in the worktree
212+ # @param normalize_path [Boolean] if true, path is converted to an absolute path to the root of the working tree
213+ #
214+ # The purpose of this flag is to allow tests to not have to mock the
215+ # normalization of the path. This allows testing that the right git command
216+ # is contructed based on the options passed any particular method.
223217 #
218+ # @raise [ArgumentError] if the path is not a directory or the path is not in a
219+ # git working tree
220+ #
221+ # @return [RubyGit::Worktree] the worktree whose root is at `path`
224222 # @api private
225223 #
226- def initialize ( worktree_path )
227- raise RubyGit ::Error , "Path '#{ worktree_path } ' not valid." unless File . directory? ( worktree_path )
224+ def initialize ( worktree_path , normalize_path : true )
225+ @normalize_path = normalize_path
226+
227+ @path =
228+ if normalize_path?
229+ normalize_worktree_path ( worktree_path )
230+ else
231+ worktree_path
232+ end
228233
229- @path = root_path ( worktree_path )
230234 RubyGit . logger . debug ( "Created #{ inspect } " )
231235 end
232236
233- # Find the root path of a Git working tree containing `path`
237+ # Get path of the cloned worktree from `git clone` stderr output
238+ #
239+ # @param clone_output [String] the stderr output of the `git clone` command
240+ #
241+ # @return [String] the path of the cloned worktree
242+ #
243+ # @api private
244+ private_class_method def self . cloned_to ( clone_output )
245+ clone_output . match ( /Cloning into ['"](.+)['"]\. \. \. / ) [ 1 ]
246+ end
247+
248+ # True if the path should be normalized
249+ #
250+ # This means that the path should be expanded and converted to a absolute, real
251+ # path to the working tree root dir.
252+ #
253+ # @return [Boolean]
254+ #
255+ # @api private
256+ #
257+ def normalize_path? = @normalize_path
258+
259+ # Return the absolute path to the root of the working tree containing path
260+ #
261+ # @example Expand the path
262+ # normalize_path('~/worktree') #=> '/Users/james/worktree'
263+ #
264+ # @example Convert to an absolute path
265+ # File.chdir('/User/james/worktree')
266+ # normalize_path('.') #=> '/User/james/worktree'
234267 #
235- # @raise [RubyGit::FailedError] if the path is not in a Git working tree
268+ # @param path [String] a (possibly relative) path within the worktree
269+ #
270+ # @return [String]
271+ #
272+ # @raise [ArgumentError] if the path is not a directory or the path is not in a
273+ # git working tree
274+ #
275+ # @api private
276+ #
277+ def normalize_worktree_path ( path )
278+ raise ArgumentError , "Directory '#{ path } ' does not exist." unless File . directory? ( path )
279+
280+ begin
281+ root_path ( path )
282+ rescue RubyGit ::FailedError => e
283+ raise ArgumentError , e . message
284+ end
285+ end
286+
287+ # Find the root path of a Git working tree containing `path`
236288 #
237289 # @return [String] the root path of the Git working tree containing `path`
238290 #
291+ # @raise [ArgumentError] if the path is not in a Git working tree
292+ #
239293 # @api private
240294 #
241295 def root_path ( worktree_path )
242296 command = %w[ rev-parse --show-toplevel ]
243297 options = { chdir : worktree_path , chomp : true , out : StringIO . new , err : StringIO . new }
244- File . realpath ( RubyGit ::CommandLine . run ( *command , **options ) . stdout )
298+ root_path = RubyGit ::CommandLine . run ( *command , **options ) . stdout
299+ File . realpath ( File . expand_path ( root_path ) )
245300 end
246301
247302 # Run a Git command in this worktree
@@ -255,7 +310,7 @@ def root_path(worktree_path)
255310 #
256311 # @api private
257312 #
258- def run ( *command , **options )
313+ def run_with_context ( *command , **options )
259314 RubyGit ::CommandLine . run ( *command , repository_path : repository . path , worktree_path : path , **options )
260315 end
261316
0 commit comments