module Git::PathResolver
Resolves and normalizes the filesystem paths that locate a Git repository
‘PathResolver` is the single home for the path-resolution logic used by the `Git` factory methods ({Git.open}, {Git.bare}, {Git.init}, and {Git.clone}). It computes the absolute working-directory, repository (`.git`), and index paths from the caller-supplied values, following the same rules Git itself uses (including gitdir-pointer files for submodules and linked worktrees).
A relative path given by the caller is expanded against the process working directory, as git does for a path on its command line. Only the defaults are derived from the layout: an omitted repository is ‘<working_directory>/.git` and an omitted index is `<repository>/index`.
@api private
Constants
- CHDIR_MESSAGE
-
The {Git::Error} message when the ‘:chdir` option of `Git.clone` cannot be expanded
- INDEX_MESSAGE
-
The {Git::Error} message when an index file path cannot be expanded
- REPOSITORY_MESSAGE
-
The {Git::Error} message when a repository directory path cannot be expanded
- WORKING_DIRECTORY_MESSAGE
-
The {Git::Error} message when a working directory path cannot be expanded
Public Instance Methods
Source
# File lib/git/path_resolver.rb, line 130 def execute_rev_parse_toplevel(working_dir, binary_path: :use_global_config, git_ssh: :use_global_config) execution_context = Git::ExecutionContext::Global.new(binary_path: binary_path, git_ssh: git_ssh) Git::Commands::RevParse.new(execution_context).call(show_toplevel: true, chdir: working_dir).stdout rescue Git::FailedError raise ArgumentError, "'#{working_dir}' is not in a git working tree" end
Run ‘git rev-parse –show-toplevel` from `working_dir` and return stdout
@param working_dir [String] the absolute path of a directory inside the
working tree
@param binary_path [String, :use_global_config] path to the git binary
@param git_ssh [String, nil, :use_global_config] the SSH wrapper path
@return [String] the top-level directory reported by git
@raise [ArgumentError] if ‘working_dir` is not inside a git working tree
@raise [Git::Error] if the git binary cannot be found or fails to launch
@api private
Source
# File lib/git/path_resolver.rb, line 270 def expand_path(path, message, base: nil) Git::SystemCallGuard.call(message) { File.expand_path(path || '.', base) } end
Expand a path the way a shell prompt would before git runs
Follows ‘File.expand_path`: `~` is the home directory, a relative path is joined onto `base` (or onto the process working directory when `base` is `nil`), `.` and `..` are normalized, and an absolute path is unchanged.
@example Expand a relative path against the process working directory
Git::PathResolver.expand_path('repo', 'Failed to resolve the working directory') #=> '/current/dir/repo'
@example Expand a relative path against another directory
Git::PathResolver.expand_path('repo', 'Failed to resolve the working directory', base: '/output') #=> '/output/repo'
@param path [String, Pathname, nil] the path to expand, or ‘nil` for
`base` itself
@param message [String] the message of the {Git::Error} raised when the
expansion fails
@param base [String, Pathname, nil] the directory a relative ‘path` is
expanded against, or `nil` for the process working directory
@return [String] the absolute path
@raise [ArgumentError] if ‘~` cannot be expanded because no home directory
can be determined, or if the path begins with `~user` for a user that does not exist
@raise [Git::Error] if the path cannot be expanded, which happens when the
process working directory has been removed
@api private
Source
# File lib/git/path_resolver.rb, line 204 def resolve_gitdir_pointer(path) return path unless File.file?(path) gitdir_content = Git::SystemCallGuard.call('Failed to read the gitdir pointer file') { File.read(path) }.strip return path unless gitdir_content.start_with?('gitdir: ') gitdir_path = gitdir_content.sub(/\Agitdir: /, '') File.expand_path(gitdir_path, File.dirname(path)) end
Resolve gitdir-pointer files used by submodules and linked worktrees
If ‘path` points to a file containing `“gitdir: <path>”`, returns the resolved target path. Otherwise returns `path` unchanged.
@param path [String] the path to check
@return [String] the resolved absolute path
@raise [Git::Error] if ‘path` is a file that cannot be read
Relative pointer targets are resolved from the directory containing the pointer file itself, matching git’s pointer-file semantics.
@api private
Source
# File lib/git/path_resolver.rb, line 229 def resolve_index(path, repository) return File.join(repository, 'index') unless path expand_path(path, INDEX_MESSAGE) end
Resolve the index file path
@param path [String, nil] the index path or ‘nil`
@param repository [String] the repository directory whose ‘index` is the
default when `path` is `nil`
@return [String] the absolute path to the index file
@raise [Git::Error] if the path cannot be expanded, which happens when the
process working directory has been removed
@api private
Source
# File lib/git/path_resolver.rb, line 67 def resolve_paths(working_directory: nil, repository: nil, index: nil, bare: false) working_dir = resolve_working_directory(working_directory, bare: bare) repo_path = resolve_repository(repository, working_dir) index_path = resolve_index(index, repo_path) { working_directory: working_dir, repository: repo_path, index: index_path } end
Resolve and normalize the paths that locate a Git repository
Returns a new hash containing the resolved absolute paths for:
* `:working_directory` — the working tree root (`nil` for bare repos) * `:repository` — the `.git` directory * `:index` — the index file
This method does not mutate any inputs.
@example Resolve paths for a working tree
Git::PathResolver.resolve_paths(working_directory: '/repo') #=> { working_directory: '/repo', repository: '/repo/.git', index: '/repo/.git/index' }
@param working_directory [String, nil] the working directory path
@param repository [String, nil] the repository (‘.git`) directory path
@param index [String, nil] the index file path
@param bare [Boolean] whether this is a bare repository
@return [Hash{Symbol => (String, nil)}] a hash with ‘:working_directory`,
`:repository`, and `:index` keys
@raise [Git::Error] if the repository path is a gitdir pointer file that
cannot be read
Source
# File lib/git/path_resolver.rb, line 176 def resolve_repository(path, working_dir) initial_path = if working_dir && path.nil? File.join(working_dir, '.git') else expand_path(path, REPOSITORY_MESSAGE) end resolve_gitdir_pointer(initial_path) end
Resolve the repository (‘.git`) directory path
Handles the gitdir-pointer file case for submodules and linked worktrees.
@param path [String, nil] the repository path or ‘nil`
@param working_dir [String, nil] the working directory whose ‘.git` is the
default when `path` is `nil`, or `nil` for a bare repository, whose default is the process working directory
@return [String] the absolute path to the repository
@raise [Git::Error] if the path cannot be expanded, which happens when the
process working directory has been removed
@api private
Source
# File lib/git/path_resolver.rb, line 152 def resolve_working_directory(path, bare:) return nil if bare expand_path(path, WORKING_DIRECTORY_MESSAGE) end
Resolve the working directory path
@param path [String, nil] the working directory path or ‘nil`
@param bare [Boolean] whether this is a bare repository
@return [String, nil] the absolute path, or ‘nil` for bare repos
@raise [Git::Error] if the path cannot be expanded, which happens when the
process working directory has been removed
@api private
Source
# File lib/git/path_resolver.rb, line 106 def root_of_worktree(working_dir, binary_path: :use_global_config, git_ssh: :use_global_config) expanded_dir = expand_path(working_dir, 'Failed to expand the working directory path') raise ArgumentError, "'#{working_dir}' does not exist or is not a directory" unless Dir.exist?(expanded_dir) execute_rev_parse_toplevel(expanded_dir, binary_path: binary_path, git_ssh: git_ssh) end
Find the root of the working tree that contains ‘working_dir`
Runs ‘git rev-parse –show-toplevel` from `working_dir` to locate the top-level directory of the working tree.
@example Find the worktree root from a subdirectory
Git::PathResolver.root_of_worktree('/repo/subdir') #=> '/repo'
@param working_dir [String] a path inside the working tree
@param binary_path [String, :use_global_config] path to the git binary
Controls which git binary is invoked during root detection. Defaults to `:use_global_config`, which resolves to `Git.config.binary_path`.
@param git_ssh [String, nil, :use_global_config] the SSH wrapper path
Forwarded as `GIT_SSH`. Defaults to `:use_global_config`.
@return [String] the absolute path to the root of the working tree
@raise [ArgumentError] if ‘working_dir` does not exist, is not a
directory, or is not inside a git working tree
@raise [Git::Error] if the git binary cannot be found or fails to launch, or
if `working_dir` cannot be expanded to an absolute path