git 1.19.1 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +5 -1
- data/AI_POLICY.md +24 -0
- data/CHANGELOG.md +689 -0
- data/CODE_OF_CONDUCT.md +25 -0
- data/CONTRIBUTING.md +1175 -97
- data/GOVERNANCE.md +106 -0
- data/LICENSE +1 -1
- data/MAINTAINERS.md +17 -4
- data/README.md +476 -320
- data/UPGRADING.md +1138 -0
- data/git.gemspec +124 -36
- data/lib/git/author.rb +39 -7
- data/lib/git/author_info.rb +66 -0
- data/lib/git/branch.rb +615 -65
- data/lib/git/branch_delete_failure.rb +34 -0
- data/lib/git/branch_delete_result.rb +66 -0
- data/lib/git/branch_info.rb +237 -0
- data/lib/git/branches.rb +167 -44
- data/lib/git/command_line/base.rb +247 -0
- data/lib/git/command_line/capturing.rb +308 -0
- data/lib/git/command_line/result.rb +88 -0
- data/lib/git/command_line/streaming.rb +236 -0
- data/lib/git/command_line.rb +52 -0
- data/lib/git/commands/add.rb +139 -0
- data/lib/git/commands/am/abort.rb +43 -0
- data/lib/git/commands/am/apply.rb +263 -0
- data/lib/git/commands/am/continue.rb +43 -0
- data/lib/git/commands/am/quit.rb +43 -0
- data/lib/git/commands/am/retry.rb +49 -0
- data/lib/git/commands/am/show_current_patch.rb +64 -0
- data/lib/git/commands/am/skip.rb +42 -0
- data/lib/git/commands/am.rb +33 -0
- data/lib/git/commands/apply.rb +242 -0
- data/lib/git/commands/archive/list_formats.rb +46 -0
- data/lib/git/commands/archive.rb +145 -0
- data/lib/git/commands/arguments.rb +4521 -0
- data/lib/git/commands/base.rb +502 -0
- data/lib/git/commands/branch/copy.rb +102 -0
- data/lib/git/commands/branch/create.rb +177 -0
- data/lib/git/commands/branch/delete.rb +88 -0
- data/lib/git/commands/branch/list.rb +178 -0
- data/lib/git/commands/branch/move.rb +102 -0
- data/lib/git/commands/branch/set_upstream.rb +86 -0
- data/lib/git/commands/branch/show_current.rb +49 -0
- data/lib/git/commands/branch/unset_upstream.rb +53 -0
- data/lib/git/commands/branch.rb +34 -0
- data/lib/git/commands/cat_file/batch.rb +385 -0
- data/lib/git/commands/cat_file/filtered.rb +105 -0
- data/lib/git/commands/cat_file/raw.rb +271 -0
- data/lib/git/commands/cat_file.rb +49 -0
- data/lib/git/commands/checkout/branch.rb +153 -0
- data/lib/git/commands/checkout/files.rb +116 -0
- data/lib/git/commands/checkout.rb +38 -0
- data/lib/git/commands/checkout_index.rb +106 -0
- data/lib/git/commands/clean.rb +102 -0
- data/lib/git/commands/clone.rb +241 -0
- data/lib/git/commands/commit.rb +273 -0
- data/lib/git/commands/commit_tree.rb +101 -0
- data/lib/git/commands/config_option_syntax/add.rb +86 -0
- data/lib/git/commands/config_option_syntax/get.rb +121 -0
- data/lib/git/commands/config_option_syntax/get_all.rb +118 -0
- data/lib/git/commands/config_option_syntax/get_color.rb +95 -0
- data/lib/git/commands/config_option_syntax/get_color_bool.rb +96 -0
- data/lib/git/commands/config_option_syntax/get_regexp.rb +119 -0
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +111 -0
- data/lib/git/commands/config_option_syntax/list.rb +111 -0
- data/lib/git/commands/config_option_syntax/remove_section.rb +79 -0
- data/lib/git/commands/config_option_syntax/rename_section.rb +83 -0
- data/lib/git/commands/config_option_syntax/replace_all.rb +109 -0
- data/lib/git/commands/config_option_syntax/set.rb +119 -0
- data/lib/git/commands/config_option_syntax/unset.rb +92 -0
- data/lib/git/commands/config_option_syntax/unset_all.rb +94 -0
- data/lib/git/commands/config_option_syntax.rb +56 -0
- data/lib/git/commands/describe.rb +156 -0
- data/lib/git/commands/diff.rb +657 -0
- data/lib/git/commands/diff_files.rb +519 -0
- data/lib/git/commands/diff_index.rb +499 -0
- data/lib/git/commands/fetch.rb +354 -0
- data/lib/git/commands/fsck.rb +138 -0
- data/lib/git/commands/gc.rb +134 -0
- data/lib/git/commands/grep.rb +339 -0
- data/lib/git/commands/init.rb +101 -0
- data/lib/git/commands/log.rb +634 -0
- data/lib/git/commands/ls_files.rb +195 -0
- data/lib/git/commands/ls_remote.rb +161 -0
- data/lib/git/commands/ls_tree.rb +135 -0
- data/lib/git/commands/maintenance/register.rb +77 -0
- data/lib/git/commands/maintenance/run.rb +109 -0
- data/lib/git/commands/maintenance/start.rb +71 -0
- data/lib/git/commands/maintenance/stop.rb +60 -0
- data/lib/git/commands/maintenance/unregister.rb +84 -0
- data/lib/git/commands/maintenance.rb +31 -0
- data/lib/git/commands/merge/abort.rb +44 -0
- data/lib/git/commands/merge/continue.rb +44 -0
- data/lib/git/commands/merge/quit.rb +46 -0
- data/lib/git/commands/merge/start.rb +250 -0
- data/lib/git/commands/merge.rb +28 -0
- data/lib/git/commands/merge_base.rb +91 -0
- data/lib/git/commands/mv.rb +82 -0
- data/lib/git/commands/name_rev.rb +119 -0
- data/lib/git/commands/pull.rb +382 -0
- data/lib/git/commands/push.rb +251 -0
- data/lib/git/commands/read_tree.rb +154 -0
- data/lib/git/commands/remote/add.rb +96 -0
- data/lib/git/commands/remote/get_url.rb +68 -0
- data/lib/git/commands/remote/list.rb +56 -0
- data/lib/git/commands/remote/prune.rb +63 -0
- data/lib/git/commands/remote/remove.rb +52 -0
- data/lib/git/commands/remote/rename.rb +76 -0
- data/lib/git/commands/remote/set_branches.rb +70 -0
- data/lib/git/commands/remote/set_head.rb +89 -0
- data/lib/git/commands/remote/set_url.rb +78 -0
- data/lib/git/commands/remote/set_url_add.rb +70 -0
- data/lib/git/commands/remote/set_url_delete.rb +71 -0
- data/lib/git/commands/remote/show.rb +77 -0
- data/lib/git/commands/remote/update.rb +79 -0
- data/lib/git/commands/remote.rb +42 -0
- data/lib/git/commands/repack.rb +281 -0
- data/lib/git/commands/reset.rb +154 -0
- data/lib/git/commands/rev_parse.rb +304 -0
- data/lib/git/commands/revert/abort.rb +45 -0
- data/lib/git/commands/revert/continue.rb +62 -0
- data/lib/git/commands/revert/quit.rb +47 -0
- data/lib/git/commands/revert/skip.rb +44 -0
- data/lib/git/commands/revert/start.rb +158 -0
- data/lib/git/commands/revert.rb +29 -0
- data/lib/git/commands/rm.rb +113 -0
- data/lib/git/commands/show.rb +632 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +119 -0
- data/lib/git/commands/show_ref/exists.rb +80 -0
- data/lib/git/commands/show_ref/list.rb +149 -0
- data/lib/git/commands/show_ref/verify.rb +122 -0
- data/lib/git/commands/show_ref.rb +42 -0
- data/lib/git/commands/stash/apply.rb +81 -0
- data/lib/git/commands/stash/branch.rb +67 -0
- data/lib/git/commands/stash/clear.rb +43 -0
- data/lib/git/commands/stash/create.rb +60 -0
- data/lib/git/commands/stash/drop.rb +73 -0
- data/lib/git/commands/stash/list.rb +43 -0
- data/lib/git/commands/stash/pop.rb +87 -0
- data/lib/git/commands/stash/push.rb +112 -0
- data/lib/git/commands/stash/show.rb +158 -0
- data/lib/git/commands/stash/store.rb +72 -0
- data/lib/git/commands/stash.rb +38 -0
- data/lib/git/commands/status.rb +174 -0
- data/lib/git/commands/symbolic_ref/delete.rb +72 -0
- data/lib/git/commands/symbolic_ref/read.rb +99 -0
- data/lib/git/commands/symbolic_ref/update.rb +79 -0
- data/lib/git/commands/symbolic_ref.rb +38 -0
- data/lib/git/commands/tag/create.rb +142 -0
- data/lib/git/commands/tag/delete.rb +57 -0
- data/lib/git/commands/tag/list.rb +146 -0
- data/lib/git/commands/tag/verify.rb +71 -0
- data/lib/git/commands/tag.rb +26 -0
- data/lib/git/commands/update_ref/batch.rb +145 -0
- data/lib/git/commands/update_ref/delete.rb +90 -0
- data/lib/git/commands/update_ref/update.rb +103 -0
- data/lib/git/commands/update_ref.rb +42 -0
- data/lib/git/commands/version.rb +60 -0
- data/lib/git/commands/worktree/add.rb +139 -0
- data/lib/git/commands/worktree/list.rb +64 -0
- data/lib/git/commands/worktree/lock.rb +58 -0
- data/lib/git/commands/worktree/management_base.rb +51 -0
- data/lib/git/commands/worktree/move.rb +66 -0
- data/lib/git/commands/worktree/prune.rb +67 -0
- data/lib/git/commands/worktree/remove.rb +63 -0
- data/lib/git/commands/worktree/repair.rb +76 -0
- data/lib/git/commands/worktree/unlock.rb +47 -0
- data/lib/git/commands/worktree.rb +43 -0
- data/lib/git/commands/write_tree.rb +68 -0
- data/lib/git/commands.rb +88 -0
- data/lib/git/config.rb +72 -5
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +795 -0
- data/lib/git/detached_head_info.rb +57 -0
- data/lib/git/diff.rb +437 -86
- data/lib/git/diff_file_numstat_info.rb +31 -0
- data/lib/git/diff_file_patch_info.rb +136 -0
- data/lib/git/diff_file_raw_info.rb +129 -0
- data/lib/git/diff_info.rb +162 -0
- data/lib/git/diff_path_status.rb +107 -0
- data/lib/git/diff_result.rb +34 -0
- data/lib/git/diff_stats.rb +111 -0
- data/lib/git/dirstat_info.rb +102 -0
- data/lib/git/encoding_utils.rb +32 -1
- data/lib/git/errors.rb +285 -0
- data/lib/git/escaped_path.rb +57 -5
- data/lib/git/execution_context/global.rb +31 -0
- data/lib/git/execution_context/repository.rb +151 -0
- data/lib/git/execution_context.rb +559 -0
- data/lib/git/factories.rb +813 -0
- data/lib/git/file_ref.rb +77 -0
- data/lib/git/fsck_object.rb +56 -0
- data/lib/git/fsck_result.rb +132 -0
- data/lib/git/log.rb +306 -90
- data/lib/git/object.rb +563 -141
- data/lib/git/parsers/branch.rb +240 -0
- data/lib/git/parsers/cat_file.rb +111 -0
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/diff.rb +792 -0
- data/lib/git/parsers/fsck.rb +144 -0
- data/lib/git/parsers/grep.rb +42 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/parsers/ls_tree.rb +58 -0
- data/lib/git/parsers/remote.rb +162 -0
- data/lib/git/parsers/stash.rb +292 -0
- data/lib/git/parsers/status.rb +251 -0
- data/lib/git/parsers/tag.rb +341 -0
- data/lib/git/parsers/worktree.rb +185 -0
- data/lib/git/path_resolver.rb +206 -0
- data/lib/git/remote.rb +165 -12
- data/lib/git/remote_info.rb +203 -0
- data/lib/git/repository/branching.rb +964 -0
- data/lib/git/repository/committing.rb +246 -0
- data/lib/git/repository/context_helpers.rb +293 -0
- data/lib/git/repository/diffing.rb +785 -0
- data/lib/git/repository/inspecting.rb +252 -0
- data/lib/git/repository/logging.rb +410 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +451 -0
- data/lib/git/repository/object_operations.rb +1551 -0
- data/lib/git/repository/remote_operations.rb +984 -0
- data/lib/git/repository/shared_private.rb +120 -0
- data/lib/git/repository/staging.rb +587 -0
- data/lib/git/repository/stashing.rb +623 -0
- data/lib/git/repository/status_operations.rb +249 -0
- data/lib/git/repository/worktree_operations.rb +339 -0
- data/lib/git/repository.rb +484 -2
- data/lib/git/stash.rb +109 -12
- data/lib/git/stash_info.rb +102 -0
- data/lib/git/stashes.rb +169 -26
- data/lib/git/status.rb +308 -122
- data/lib/git/status_file_info.rb +258 -0
- data/lib/git/status_info.rb +189 -0
- data/lib/git/tag_delete_failure.rb +34 -0
- data/lib/git/tag_delete_result.rb +66 -0
- data/lib/git/tag_info.rb +99 -0
- data/lib/git/url.rb +15 -8
- data/lib/git/version.rb +113 -2
- data/lib/git/version_constraint.rb +85 -0
- data/lib/git/worktree.rb +150 -8
- data/lib/git/worktree_info.rb +128 -0
- data/lib/git/worktrees.rb +118 -13
- data/lib/git.rb +632 -234
- metadata +369 -54
- data/.github/stale.yml +0 -25
- data/.github/workflows/continuous_integration.yml +0 -49
- data/.gitignore +0 -10
- data/Dockerfile.changelog-rs +0 -12
- data/Gemfile +0 -5
- data/ISSUE_TEMPLATE.md +0 -15
- data/PULL_REQUEST_TEMPLATE.md +0 -9
- data/RELEASING.md +0 -70
- data/Rakefile +0 -60
- data/lib/git/base/factory.rb +0 -99
- data/lib/git/base.rb +0 -711
- data/lib/git/command_line_result.rb +0 -86
- data/lib/git/failed_error.rb +0 -53
- data/lib/git/git_execute_error.rb +0 -7
- data/lib/git/index.rb +0 -5
- data/lib/git/lib.rb +0 -1328
- data/lib/git/path.rb +0 -31
- data/lib/git/signaled_error.rb +0 -50
- data/lib/git/working_directory.rb +0 -4
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/commands/rev_parse'
|
|
4
|
+
require 'git/errors'
|
|
5
|
+
require 'git/execution_context'
|
|
6
|
+
require 'git/execution_context/global'
|
|
7
|
+
|
|
8
|
+
module Git
|
|
9
|
+
# Resolves and normalizes the filesystem paths that locate a Git repository
|
|
10
|
+
#
|
|
11
|
+
# `PathResolver` is the single home for the path-resolution logic used by the
|
|
12
|
+
# `Git::Repository` factory class methods ({Git::Repository.open} and
|
|
13
|
+
# {Git::Repository.bare}). It computes the absolute working-directory,
|
|
14
|
+
# repository (`.git`), and index paths from the caller-supplied values,
|
|
15
|
+
# following the same rules Git itself uses (including gitdir-pointer files for
|
|
16
|
+
# submodules and linked worktrees).
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
#
|
|
20
|
+
module PathResolver
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Resolve and normalize the paths that locate a Git repository
|
|
24
|
+
#
|
|
25
|
+
# Returns a new hash containing the resolved absolute paths for:
|
|
26
|
+
# * `:working_directory` — the working tree root (`nil` for bare repos)
|
|
27
|
+
# * `:repository` — the `.git` directory
|
|
28
|
+
# * `:index` — the index file
|
|
29
|
+
#
|
|
30
|
+
# This method does not mutate any inputs.
|
|
31
|
+
#
|
|
32
|
+
# @example Resolve paths for a working tree
|
|
33
|
+
# Git::PathResolver.resolve_paths(working_directory: '/repo')
|
|
34
|
+
# #=> { working_directory: '/repo', repository: '/repo/.git', index: '/repo/.git/index' }
|
|
35
|
+
#
|
|
36
|
+
# @param working_directory [String, nil] the working directory path
|
|
37
|
+
#
|
|
38
|
+
# @param repository [String, nil] the repository (`.git`) directory path
|
|
39
|
+
#
|
|
40
|
+
# @param index [String, nil] the index file path
|
|
41
|
+
#
|
|
42
|
+
# @param bare [Boolean] whether this is a bare repository
|
|
43
|
+
#
|
|
44
|
+
# @return [Hash{Symbol => (String, nil)}] a hash with `:working_directory`,
|
|
45
|
+
# `:repository`, and `:index` keys
|
|
46
|
+
#
|
|
47
|
+
def resolve_paths(working_directory: nil, repository: nil, index: nil, bare: false)
|
|
48
|
+
working_dir = resolve_working_directory(working_directory, bare: bare)
|
|
49
|
+
# For bare repos, use working_directory as the default repository location
|
|
50
|
+
repo_path = resolve_repository(repository, working_dir, bare: bare, bare_default: working_directory)
|
|
51
|
+
index_path = resolve_index(index, repo_path)
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
working_directory: working_dir,
|
|
55
|
+
repository: repo_path,
|
|
56
|
+
index: index_path
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Find the root of the working tree that contains `working_dir`
|
|
61
|
+
#
|
|
62
|
+
# Runs `git rev-parse --show-toplevel` from `working_dir` to locate the
|
|
63
|
+
# top-level directory of the working tree.
|
|
64
|
+
#
|
|
65
|
+
# @example Find the worktree root from a subdirectory
|
|
66
|
+
# Git::PathResolver.root_of_worktree('/repo/subdir') #=> '/repo'
|
|
67
|
+
#
|
|
68
|
+
# @param working_dir [String] a path inside the working tree
|
|
69
|
+
#
|
|
70
|
+
# @param binary_path [String, :use_global_config] path to the git binary
|
|
71
|
+
#
|
|
72
|
+
# Controls which git binary is invoked during root detection. Defaults to
|
|
73
|
+
# `:use_global_config`, which resolves to `Git.config.binary_path`.
|
|
74
|
+
#
|
|
75
|
+
# @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
|
|
76
|
+
#
|
|
77
|
+
# Forwarded as `GIT_SSH`. Defaults to `:use_global_config`.
|
|
78
|
+
#
|
|
79
|
+
# @return [String] the absolute path to the root of the working tree
|
|
80
|
+
#
|
|
81
|
+
# @raise [ArgumentError] if `working_dir` does not exist, is not a
|
|
82
|
+
# directory, or is not inside a git working tree
|
|
83
|
+
#
|
|
84
|
+
# Also raised if the git binary cannot be found.
|
|
85
|
+
#
|
|
86
|
+
def root_of_worktree(working_dir, binary_path: :use_global_config, git_ssh: :use_global_config)
|
|
87
|
+
raise ArgumentError, "'#{working_dir}' does not exist or is not a directory" unless Dir.exist?(working_dir)
|
|
88
|
+
|
|
89
|
+
execute_rev_parse_toplevel(working_dir, binary_path: binary_path, git_ssh: git_ssh)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Run `git rev-parse --show-toplevel` from `working_dir` and return stdout
|
|
93
|
+
#
|
|
94
|
+
# @param working_dir [String] a path inside the working tree
|
|
95
|
+
#
|
|
96
|
+
# @param binary_path [String, :use_global_config] path to the git binary
|
|
97
|
+
#
|
|
98
|
+
# @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
|
|
99
|
+
#
|
|
100
|
+
# @return [String] the top-level directory reported by git
|
|
101
|
+
#
|
|
102
|
+
# @raise [ArgumentError] if the git binary is not found or `working_dir` is
|
|
103
|
+
# not inside a git working tree
|
|
104
|
+
#
|
|
105
|
+
# @api private
|
|
106
|
+
#
|
|
107
|
+
def execute_rev_parse_toplevel(working_dir, binary_path: :use_global_config, git_ssh: :use_global_config)
|
|
108
|
+
execution_context = Git::ExecutionContext::Global.new(binary_path: binary_path, git_ssh: git_ssh)
|
|
109
|
+
Git::Commands::RevParse.new(execution_context).call(
|
|
110
|
+
show_toplevel: true, chdir: File.expand_path(working_dir)
|
|
111
|
+
).stdout
|
|
112
|
+
rescue Errno::ENOENT
|
|
113
|
+
raise ArgumentError, 'Failed to find the root of the worktree: git binary not found'
|
|
114
|
+
rescue Git::FailedError
|
|
115
|
+
raise ArgumentError, "'#{working_dir}' is not in a git working tree"
|
|
116
|
+
end
|
|
117
|
+
private_class_method :execute_rev_parse_toplevel
|
|
118
|
+
|
|
119
|
+
# Resolve the working directory path
|
|
120
|
+
#
|
|
121
|
+
# @param path [String, nil] the working directory path or `nil`
|
|
122
|
+
#
|
|
123
|
+
# @param bare [Boolean] whether this is a bare repository
|
|
124
|
+
#
|
|
125
|
+
# @return [String, nil] the absolute path, or `nil` for bare repos
|
|
126
|
+
#
|
|
127
|
+
# @api private
|
|
128
|
+
#
|
|
129
|
+
def resolve_working_directory(path, bare:)
|
|
130
|
+
return nil if bare
|
|
131
|
+
|
|
132
|
+
File.expand_path(path || Dir.pwd)
|
|
133
|
+
end
|
|
134
|
+
private_class_method :resolve_working_directory
|
|
135
|
+
|
|
136
|
+
# Resolve the repository (`.git`) directory path
|
|
137
|
+
#
|
|
138
|
+
# Handles the gitdir-pointer file case for submodules and linked worktrees.
|
|
139
|
+
#
|
|
140
|
+
# @param path [String, nil] the repository path or `nil`
|
|
141
|
+
#
|
|
142
|
+
# @param working_dir [String, nil] the working directory used for relative
|
|
143
|
+
# path resolution
|
|
144
|
+
#
|
|
145
|
+
# @param bare [Boolean] whether this is a bare repository
|
|
146
|
+
#
|
|
147
|
+
# @param bare_default [String, nil] for bare repos, used as the default when
|
|
148
|
+
# `path` is `nil`
|
|
149
|
+
#
|
|
150
|
+
# @return [String] the absolute path to the repository
|
|
151
|
+
#
|
|
152
|
+
# @api private
|
|
153
|
+
#
|
|
154
|
+
def resolve_repository(path, working_dir, bare:, bare_default: nil)
|
|
155
|
+
initial_path = if bare
|
|
156
|
+
File.expand_path(path || bare_default || Dir.pwd)
|
|
157
|
+
else
|
|
158
|
+
File.expand_path(path || '.git', working_dir)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
resolve_gitdir_pointer(initial_path)
|
|
162
|
+
end
|
|
163
|
+
private_class_method :resolve_repository
|
|
164
|
+
|
|
165
|
+
# Resolve gitdir-pointer files used by submodules and linked worktrees
|
|
166
|
+
#
|
|
167
|
+
# If `path` points to a file containing `"gitdir: <path>"`, returns the
|
|
168
|
+
# resolved target path. Otherwise returns `path` unchanged.
|
|
169
|
+
#
|
|
170
|
+
# @param path [String] the path to check
|
|
171
|
+
#
|
|
172
|
+
# @return [String] the resolved absolute path
|
|
173
|
+
#
|
|
174
|
+
# Relative pointer targets are resolved from the directory containing the
|
|
175
|
+
# pointer file itself, matching git's pointer-file semantics.
|
|
176
|
+
#
|
|
177
|
+
# @api private
|
|
178
|
+
#
|
|
179
|
+
def resolve_gitdir_pointer(path)
|
|
180
|
+
return path unless File.file?(path)
|
|
181
|
+
|
|
182
|
+
gitdir_content = File.read(path).strip
|
|
183
|
+
return path unless gitdir_content.start_with?('gitdir: ')
|
|
184
|
+
|
|
185
|
+
gitdir_path = gitdir_content.sub(/\Agitdir: /, '')
|
|
186
|
+
File.expand_path(gitdir_path, File.dirname(path))
|
|
187
|
+
end
|
|
188
|
+
private_class_method :resolve_gitdir_pointer
|
|
189
|
+
|
|
190
|
+
# Resolve the index file path
|
|
191
|
+
#
|
|
192
|
+
# @param path [String, nil] the index path or `nil`
|
|
193
|
+
#
|
|
194
|
+
# @param repository [String] the repository directory used for relative
|
|
195
|
+
# path resolution
|
|
196
|
+
#
|
|
197
|
+
# @return [String] the absolute path to the index file
|
|
198
|
+
#
|
|
199
|
+
# @api private
|
|
200
|
+
#
|
|
201
|
+
def resolve_index(path, repository)
|
|
202
|
+
File.expand_path(path || 'index', repository)
|
|
203
|
+
end
|
|
204
|
+
private_class_method :resolve_index
|
|
205
|
+
end
|
|
206
|
+
end
|
data/lib/git/remote.rb
CHANGED
|
@@ -1,38 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/branch'
|
|
4
|
+
require 'git/branch_info'
|
|
5
|
+
|
|
1
6
|
module Git
|
|
2
|
-
|
|
7
|
+
# A remote in a Git repository
|
|
8
|
+
#
|
|
9
|
+
# Remote objects provide access to remote metadata and operations like fetch,
|
|
10
|
+
# merge, and remove. This class and `Git::Repository#remote`, which returns
|
|
11
|
+
# it, are both deprecated: read remote configuration through
|
|
12
|
+
# {Git::Repository::RemoteOperations#remote_list} and call the
|
|
13
|
+
# repository-level operations with the remote name instead.
|
|
14
|
+
#
|
|
15
|
+
# @example Reading a remote and fetching from it without Git::Remote
|
|
16
|
+
# git = Git.open('.')
|
|
17
|
+
# origin = git.remote_list.find { |r| r.name == 'origin' } #=> Git::RemoteInfo
|
|
18
|
+
# origin.url.first
|
|
19
|
+
# git.fetch(origin.name)
|
|
20
|
+
#
|
|
21
|
+
# @deprecated Use {Git::Repository::RemoteOperations#remote_list} and the
|
|
22
|
+
# repository-level remote operations instead
|
|
23
|
+
#
|
|
24
|
+
# {Git::Repository::RemoteOperations#remote_list} returns immutable
|
|
25
|
+
# {Git::RemoteInfo} value objects. Operations that lived on this class are
|
|
26
|
+
# called on the repository with the remote name instead (for example
|
|
27
|
+
# {Git::Repository::RemoteOperations#fetch} and
|
|
28
|
+
# {Git::Repository::RemoteOperations#remote_remove}). Constructing a
|
|
29
|
+
# `Git::Remote` emits a deprecation warning.
|
|
30
|
+
#
|
|
31
|
+
# @api public
|
|
32
|
+
#
|
|
33
|
+
class Remote
|
|
34
|
+
# The name of this remote (e.g. `'origin'`)
|
|
35
|
+
#
|
|
36
|
+
# @return [String] the remote name
|
|
37
|
+
#
|
|
38
|
+
attr_accessor :name
|
|
39
|
+
|
|
40
|
+
# The URL of this remote
|
|
41
|
+
#
|
|
42
|
+
# @return [String, nil] the remote URL
|
|
43
|
+
#
|
|
44
|
+
attr_accessor :url
|
|
3
45
|
|
|
4
|
-
|
|
46
|
+
# The fetch refspec for this remote
|
|
47
|
+
#
|
|
48
|
+
# @return [String, nil] the fetch options string
|
|
49
|
+
#
|
|
50
|
+
attr_accessor :fetch_opts
|
|
5
51
|
|
|
52
|
+
# Initialize a new Remote object
|
|
53
|
+
#
|
|
54
|
+
# @param base [Git::Repository] the git repository
|
|
55
|
+
#
|
|
56
|
+
# @param name [String] the remote name (e.g. `'origin'`)
|
|
57
|
+
#
|
|
58
|
+
# @note Do not construct directly. `Git::Repository#remote` is deprecated as
|
|
59
|
+
# well; use {Git::Repository::RemoteOperations#remote_list} and the
|
|
60
|
+
# repository-level remote operations instead.
|
|
61
|
+
#
|
|
62
|
+
# @api private
|
|
63
|
+
#
|
|
6
64
|
def initialize(base, name)
|
|
65
|
+
Git::Deprecation.warn(
|
|
66
|
+
'Git::Remote is deprecated and will be removed in v6.0.0. ' \
|
|
67
|
+
'Use Git::Repository#remote_list and the repository-level remote operations instead.'
|
|
68
|
+
)
|
|
7
69
|
@base = base
|
|
8
|
-
|
|
70
|
+
# config_remote is deprecated too; silence it so one Git::Remote.new emits one warning
|
|
71
|
+
config = Git::Deprecation.silence { remote_repository.config_remote(name) }
|
|
9
72
|
@name = name
|
|
10
73
|
@url = config['url']
|
|
11
74
|
@fetch_opts = config['fetch']
|
|
12
75
|
end
|
|
13
76
|
|
|
14
|
-
|
|
15
|
-
|
|
77
|
+
# Fetches from this remote
|
|
78
|
+
#
|
|
79
|
+
# @example Fetch from origin
|
|
80
|
+
# git.remote('origin').fetch
|
|
81
|
+
#
|
|
82
|
+
# @param opts [Hash] options for the fetch command
|
|
83
|
+
#
|
|
84
|
+
# @option opts [Boolean, nil] :tags (nil) fetch all tags from the remote
|
|
85
|
+
# (`--tags`)
|
|
86
|
+
#
|
|
87
|
+
# @option opts [Boolean, nil] :prune (nil) remove remote-tracking references
|
|
88
|
+
# that no longer exist on the remote (`--prune`)
|
|
89
|
+
#
|
|
90
|
+
# @option opts [Boolean, nil] :prune_tags (nil) remove local tags that no
|
|
91
|
+
# longer exist on the remote (`--prune-tags`)
|
|
92
|
+
#
|
|
93
|
+
# @option opts [Boolean, nil] :force (nil) override the fast-forward check
|
|
94
|
+
# when using explicit refspecs (`--force`)
|
|
95
|
+
#
|
|
96
|
+
# @option opts [Boolean, nil] :update_head_ok (nil) allow `git fetch` to
|
|
97
|
+
# update the branch pointed to by `HEAD` (`--update-head-ok`)
|
|
98
|
+
#
|
|
99
|
+
# @option opts [Boolean, nil] :unshallow (nil) convert a shallow clone into a
|
|
100
|
+
# full repository (`--unshallow`)
|
|
101
|
+
#
|
|
102
|
+
# @option opts [String, Integer, nil] :depth (nil) limit history to N commits
|
|
103
|
+
# from each branch tip (`--depth=N`)
|
|
104
|
+
#
|
|
105
|
+
# @option opts [String, Array<String>, nil] :ref (nil) one or more refspecs to
|
|
106
|
+
# fetch as positional arguments after the remote name
|
|
107
|
+
#
|
|
108
|
+
# @return [String] git's stdout from the fetch
|
|
109
|
+
#
|
|
110
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
111
|
+
#
|
|
112
|
+
def fetch(opts = {})
|
|
113
|
+
remote_repository.fetch(@name, opts)
|
|
16
114
|
end
|
|
17
115
|
|
|
18
|
-
#
|
|
19
|
-
|
|
116
|
+
# Merges this remote into the given (or current) local branch
|
|
117
|
+
#
|
|
118
|
+
# @example Merge origin/main into the current branch
|
|
119
|
+
# git.remote('origin').merge('main')
|
|
120
|
+
#
|
|
121
|
+
# @param branch [String] the local branch to merge into (defaults to current branch)
|
|
122
|
+
#
|
|
123
|
+
# @return [String] git's stdout from the merge
|
|
124
|
+
#
|
|
125
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
126
|
+
#
|
|
127
|
+
def merge(branch = nil)
|
|
128
|
+
branch ||= remote_repository.current_branch
|
|
20
129
|
remote_tracking_branch = "#{@name}/#{branch}"
|
|
21
|
-
|
|
130
|
+
remote_repository.merge(remote_tracking_branch)
|
|
22
131
|
end
|
|
23
132
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
133
|
+
# Returns a {Git::Branch} object for the given branch on this remote
|
|
134
|
+
#
|
|
135
|
+
# @example Get the remote-tracking branch object
|
|
136
|
+
# git.remote('origin').branch('main') #=> #<Git::Branch 'origin/main'>
|
|
137
|
+
#
|
|
138
|
+
# @param branch [String] the branch name on this remote (defaults to current branch)
|
|
139
|
+
#
|
|
140
|
+
# @return [Git::Branch] a branch object representing `<remote>/<branch>`
|
|
141
|
+
#
|
|
142
|
+
# @deprecated Use
|
|
143
|
+
# `Git::Repository#branch_list("#{name}/#{branch || current_branch}").first`
|
|
144
|
+
# instead
|
|
145
|
+
#
|
|
146
|
+
# With no argument this method falls back to the current branch, so the
|
|
147
|
+
# replacement has to supply `Git::Repository#current_branch` itself. The
|
|
148
|
+
# replacement returns a {Git::BranchInfo} value object rather than a
|
|
149
|
+
# {Git::Branch}, and returns `nil` when the remote-tracking branch does
|
|
150
|
+
# not exist.
|
|
151
|
+
#
|
|
152
|
+
def branch(branch = nil)
|
|
153
|
+
branch ||= remote_repository.current_branch
|
|
154
|
+
Git::Branch.new(@base, "#{@name}/#{branch}")
|
|
27
155
|
end
|
|
28
156
|
|
|
157
|
+
# Removes this remote from the repository
|
|
158
|
+
#
|
|
159
|
+
# @example Remove the upstream remote
|
|
160
|
+
# git.remote('upstream').remove
|
|
161
|
+
#
|
|
162
|
+
# @return [Git::CommandLine::Result] the result of `git remote remove`
|
|
163
|
+
#
|
|
164
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
165
|
+
#
|
|
29
166
|
def remove
|
|
30
|
-
|
|
167
|
+
remote_repository.remote_remove(@name)
|
|
31
168
|
end
|
|
32
169
|
|
|
170
|
+
# Returns the name of this remote as a string
|
|
171
|
+
#
|
|
172
|
+
# @example Get the remote name as a string
|
|
173
|
+
# git.remote('origin').to_s #=> 'origin'
|
|
174
|
+
#
|
|
175
|
+
# @return [String] the remote name
|
|
176
|
+
#
|
|
33
177
|
def to_s
|
|
34
178
|
@name
|
|
35
179
|
end
|
|
36
180
|
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
# @return [Git::Repository]
|
|
184
|
+
#
|
|
185
|
+
# @api private
|
|
186
|
+
#
|
|
187
|
+
def remote_repository
|
|
188
|
+
@base
|
|
189
|
+
end
|
|
37
190
|
end
|
|
38
191
|
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
# Value object representing a configured git remote
|
|
5
|
+
#
|
|
6
|
+
# Each instance holds the parsed configuration for a single remote as read
|
|
7
|
+
# from the repository's git config. Multi-value fields (`:url`, `:push_url`,
|
|
8
|
+
# `:fetch`, `:push`) are always `Array<String>` (never `nil`; may be empty).
|
|
9
|
+
# Those arrays are frozen copies of the values given, so the set of URLs and
|
|
10
|
+
# refspecs cannot change after construction; use `with` to derive a modified
|
|
11
|
+
# copy. The immutability is shallow, as with any `Data` member: the strings
|
|
12
|
+
# inside those arrays and the scalar members are the objects the caller
|
|
13
|
+
# passed in, not copies. All other fields are nilable except `:name`.
|
|
14
|
+
#
|
|
15
|
+
# @example Minimal remote (fetch-only, one URL)
|
|
16
|
+
# info = Git::RemoteInfo.new(
|
|
17
|
+
# name: 'origin',
|
|
18
|
+
# url: ['https://github.com/ruby-git/ruby-git.git'],
|
|
19
|
+
# push_url: [],
|
|
20
|
+
# fetch: ['+refs/heads/*:refs/remotes/origin/*'],
|
|
21
|
+
# push: []
|
|
22
|
+
# )
|
|
23
|
+
# info.name # => "origin"
|
|
24
|
+
# info.url # => ["https://github.com/ruby-git/ruby-git.git"]
|
|
25
|
+
# info.prune # => nil
|
|
26
|
+
#
|
|
27
|
+
# @api public
|
|
28
|
+
#
|
|
29
|
+
# @!attribute [r] name
|
|
30
|
+
# @return [String] the name of the remote (e.g. `'origin'`)
|
|
31
|
+
#
|
|
32
|
+
# @!attribute [r] url
|
|
33
|
+
# @return [Array<String>] the fetch URL(s) for this remote (`remote.<name>.url`)
|
|
34
|
+
#
|
|
35
|
+
# @!attribute [r] push_url
|
|
36
|
+
# @return [Array<String>] the push URL(s) for this remote (`remote.<name>.pushurl`)
|
|
37
|
+
#
|
|
38
|
+
# @!attribute [r] fetch
|
|
39
|
+
# @return [Array<String>] the fetch refspec(s) for this remote (`remote.<name>.fetch`)
|
|
40
|
+
#
|
|
41
|
+
# @!attribute [r] push
|
|
42
|
+
# @return [Array<String>] the push refspec(s) for this remote (`remote.<name>.push`)
|
|
43
|
+
#
|
|
44
|
+
# @!attribute [r] mirror
|
|
45
|
+
# @return [Boolean, nil] `true`/`false` per `remote.<name>.mirror`, or `nil` when not set
|
|
46
|
+
#
|
|
47
|
+
# @!attribute [r] skip_default_update
|
|
48
|
+
# @return [Boolean, nil] `true`/`false` per `remote.<name>.skipDefaultUpdate`, or `nil` when not set
|
|
49
|
+
#
|
|
50
|
+
# @!attribute [r] tag_opt
|
|
51
|
+
# @return [String, nil] the tag-fetching option (`remote.<name>.tagOpt`), or `nil` when not set
|
|
52
|
+
#
|
|
53
|
+
# @!attribute [r] prune
|
|
54
|
+
# @return [Boolean, nil] `true`/`false` per `remote.<name>.prune`; `nil` inherits `fetch.prune`
|
|
55
|
+
#
|
|
56
|
+
# @!attribute [r] prune_tags
|
|
57
|
+
# @return [Boolean, nil] `true`/`false` per `remote.<name>.pruneTags`; `nil` inherits `fetch.pruneTags`
|
|
58
|
+
#
|
|
59
|
+
# @!attribute [r] receivepack
|
|
60
|
+
# @return [String, nil] the `git-receive-pack` path on the remote (`remote.<name>.receivepack`)
|
|
61
|
+
#
|
|
62
|
+
# @!attribute [r] uploadpack
|
|
63
|
+
# @return [String, nil] the `git-upload-pack` path on the remote (`remote.<name>.uploadpack`)
|
|
64
|
+
#
|
|
65
|
+
# @!attribute [r] promisor
|
|
66
|
+
# @return [Boolean, nil] `true`/`false` per `remote.<name>.promisor`, or `nil` when not set
|
|
67
|
+
#
|
|
68
|
+
# @!attribute [r] partial_clone_filter
|
|
69
|
+
# @return [String, nil] the partial-clone object filter (`remote.<name>.partialclonefilter`)
|
|
70
|
+
#
|
|
71
|
+
# @!attribute [r] vcs
|
|
72
|
+
# @return [String, nil] the VCS type for git-remote helpers (`remote.<name>.vcs`)
|
|
73
|
+
#
|
|
74
|
+
RemoteInfo = Data.define(
|
|
75
|
+
:name,
|
|
76
|
+
:url,
|
|
77
|
+
:push_url,
|
|
78
|
+
:fetch,
|
|
79
|
+
:push,
|
|
80
|
+
:mirror,
|
|
81
|
+
:skip_default_update,
|
|
82
|
+
:tag_opt,
|
|
83
|
+
:prune,
|
|
84
|
+
:prune_tags,
|
|
85
|
+
:receivepack,
|
|
86
|
+
:uploadpack,
|
|
87
|
+
:promisor,
|
|
88
|
+
:partial_clone_filter,
|
|
89
|
+
:vcs
|
|
90
|
+
) do
|
|
91
|
+
# Create a new RemoteInfo
|
|
92
|
+
#
|
|
93
|
+
# @param name [String] the name of the remote (required)
|
|
94
|
+
#
|
|
95
|
+
# @param url [Array<String>] fetch URLs (default `[]`); stored as a frozen copy
|
|
96
|
+
#
|
|
97
|
+
# @param push_url [Array<String>] push URLs (default `[]`); stored as a frozen copy
|
|
98
|
+
#
|
|
99
|
+
# @param fetch [Array<String>] fetch refspecs (default `[]`); stored as a frozen copy
|
|
100
|
+
#
|
|
101
|
+
# @param push [Array<String>] push refspecs (default `[]`); stored as a frozen copy
|
|
102
|
+
#
|
|
103
|
+
# @param mirror [Boolean, nil] mirror flag (default `nil`)
|
|
104
|
+
#
|
|
105
|
+
# @param skip_default_update [Boolean, nil] skip-default-update flag (default `nil`)
|
|
106
|
+
#
|
|
107
|
+
# @param tag_opt [String, nil] tag-fetching option (default `nil`)
|
|
108
|
+
#
|
|
109
|
+
# @param prune [Boolean, nil] prune flag (default `nil`)
|
|
110
|
+
#
|
|
111
|
+
# @param prune_tags [Boolean, nil] prune-tags flag (default `nil`)
|
|
112
|
+
#
|
|
113
|
+
# @param receivepack [String, nil] receive-pack path (default `nil`)
|
|
114
|
+
#
|
|
115
|
+
# @param uploadpack [String, nil] upload-pack path (default `nil`)
|
|
116
|
+
#
|
|
117
|
+
# @param promisor [Boolean, nil] promisor flag (default `nil`)
|
|
118
|
+
#
|
|
119
|
+
# @param partial_clone_filter [String, nil] partial-clone filter (default `nil`)
|
|
120
|
+
#
|
|
121
|
+
# @param vcs [String, nil] VCS type (default `nil`)
|
|
122
|
+
#
|
|
123
|
+
# @return [Git::RemoteInfo]
|
|
124
|
+
#
|
|
125
|
+
def initialize( # rubocop:disable Metrics/ParameterLists
|
|
126
|
+
name:,
|
|
127
|
+
url: [],
|
|
128
|
+
push_url: [],
|
|
129
|
+
fetch: [],
|
|
130
|
+
push: [],
|
|
131
|
+
mirror: nil,
|
|
132
|
+
skip_default_update: nil,
|
|
133
|
+
tag_opt: nil,
|
|
134
|
+
prune: nil,
|
|
135
|
+
prune_tags: nil,
|
|
136
|
+
receivepack: nil,
|
|
137
|
+
uploadpack: nil,
|
|
138
|
+
promisor: nil,
|
|
139
|
+
partial_clone_filter: nil,
|
|
140
|
+
vcs: nil
|
|
141
|
+
)
|
|
142
|
+
super(
|
|
143
|
+
name:, url: Array(url).dup.freeze, push_url: Array(push_url).dup.freeze,
|
|
144
|
+
fetch: Array(fetch).dup.freeze, push: Array(push).dup.freeze, mirror:,
|
|
145
|
+
skip_default_update:, tag_opt:, prune:, prune_tags:, receivepack:, uploadpack:,
|
|
146
|
+
promisor:, partial_clone_filter:, vcs:
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Return a copy of this RemoteInfo with the given fields replaced
|
|
151
|
+
#
|
|
152
|
+
# Routes through {#initialize} so the multi-value fields of the copy are
|
|
153
|
+
# frozen copies, the same as on construction. `Data#with` bypasses
|
|
154
|
+
# `initialize` on Ruby 3.2, which would leave those arrays mutable.
|
|
155
|
+
#
|
|
156
|
+
# @example Replace the fetch URL
|
|
157
|
+
# info.with(url: ['https://example.com/other.git']).url
|
|
158
|
+
# # => ["https://example.com/other.git"]
|
|
159
|
+
#
|
|
160
|
+
# @param fields [Hash{Symbol => Object}] the fields to replace, keyed by
|
|
161
|
+
# member name
|
|
162
|
+
#
|
|
163
|
+
# @option fields [String] :name the name of the remote
|
|
164
|
+
#
|
|
165
|
+
# @option fields [Array<String>] :url fetch URLs
|
|
166
|
+
#
|
|
167
|
+
# @option fields [Array<String>] :push_url push URLs
|
|
168
|
+
#
|
|
169
|
+
# @option fields [Array<String>] :fetch fetch refspecs
|
|
170
|
+
#
|
|
171
|
+
# @option fields [Array<String>] :push push refspecs
|
|
172
|
+
#
|
|
173
|
+
# @option fields [Boolean, nil] :mirror mirror flag
|
|
174
|
+
#
|
|
175
|
+
# @option fields [Boolean, nil] :skip_default_update skip-default-update flag
|
|
176
|
+
#
|
|
177
|
+
# @option fields [String, nil] :tag_opt tag-fetching option
|
|
178
|
+
#
|
|
179
|
+
# @option fields [Boolean, nil] :prune prune flag
|
|
180
|
+
#
|
|
181
|
+
# @option fields [Boolean, nil] :prune_tags prune-tags flag
|
|
182
|
+
#
|
|
183
|
+
# @option fields [String, nil] :receivepack receive-pack path
|
|
184
|
+
#
|
|
185
|
+
# @option fields [String, nil] :uploadpack upload-pack path
|
|
186
|
+
#
|
|
187
|
+
# @option fields [Boolean, nil] :promisor promisor flag
|
|
188
|
+
#
|
|
189
|
+
# @option fields [String, nil] :partial_clone_filter partial-clone filter
|
|
190
|
+
#
|
|
191
|
+
# @option fields [String, nil] :vcs VCS type
|
|
192
|
+
#
|
|
193
|
+
# @return [Git::RemoteInfo] a new instance; `self` when no fields are given
|
|
194
|
+
#
|
|
195
|
+
# @raise [ArgumentError] if a key is not a member of this Data class
|
|
196
|
+
#
|
|
197
|
+
def with(**fields)
|
|
198
|
+
return self if fields.empty?
|
|
199
|
+
|
|
200
|
+
self.class.new(**to_h, **fields)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|