git 5.1.0 → 5.2.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/CHANGELOG.md +45 -0
- data/CONTRIBUTING.md +185 -165
- data/LICENSE +1 -1
- data/README.md +151 -151
- data/git.gemspec +35 -3
- data/lib/git/command_line/base.rb +1 -2
- data/lib/git/commands/base.rb +1 -1
- data/lib/git/commands/cat_file/raw.rb +8 -7
- data/lib/git/repository/branching.rb +79 -5
- data/lib/git/version.rb +1 -1
- metadata +5 -94
- data/.claude/commands/address-copilot-reviews.md +0 -14
- data/.claude/settings.json +0 -16
- data/.claude/skills +0 -1
- data/.commitlintrc.yml +0 -38
- data/.dockerignore +0 -27
- data/.github/copilot-instructions.md +0 -30
- data/.github/hooks/bin-setup-on-worktree.json +0 -11
- data/.github/hooks/run-bin-setup-once.sh +0 -20
- data/.github/issue_template.md +0 -15
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +0 -188
- data/.github/pull_request_template.md +0 -21
- data/.github/skills/breaking-change-analysis/SKILL.md +0 -99
- data/.github/skills/ci-cd-troubleshooting/SKILL.md +0 -264
- data/.github/skills/command-implementation/REFERENCE.md +0 -994
- data/.github/skills/command-implementation/SKILL.md +0 -230
- data/.github/skills/command-test-conventions/SKILL.md +0 -664
- data/.github/skills/command-yard-documentation/SKILL.md +0 -434
- data/.github/skills/dependency-management/SKILL.md +0 -72
- data/.github/skills/development-workflow/SKILL.md +0 -512
- data/.github/skills/facade-implementation/REFERENCE.md +0 -837
- data/.github/skills/facade-implementation/SKILL.md +0 -269
- data/.github/skills/facade-test-conventions/SKILL.md +0 -391
- data/.github/skills/facade-yard-documentation/SKILL.md +0 -435
- data/.github/skills/make-skill-template/SKILL.md +0 -226
- data/.github/skills/pr-readiness-review/SKILL.md +0 -205
- data/.github/skills/project-context/SKILL.md +0 -306
- data/.github/skills/pull-request-review/SKILL.md +0 -168
- data/.github/skills/rebase/SKILL.md +0 -148
- data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +0 -131
- data/.github/skills/release-management/SKILL.md +0 -125
- data/.github/skills/resolve-feedback/SKILL.md +0 -288
- data/.github/skills/review-arguments-dsl/CHECKLIST.md +0 -788
- data/.github/skills/review-arguments-dsl/SKILL.md +0 -214
- data/.github/skills/review-cross-command-consistency/SKILL.md +0 -139
- data/.github/skills/reviewing-skills/SKILL.md +0 -214
- data/.github/skills/rspec-unit-testing-standards/SKILL.md +0 -685
- data/.github/skills/tdd-refactor-step/SKILL.md +0 -236
- data/.github/skills/test-debugging/SKILL.md +0 -161
- data/.github/skills/yard-documentation/SKILL.md +0 -981
- data/.github/skills/yard-documentation/element-rules.md +0 -162
- data/.github/skills-deprecated/README.md +0 -21
- data/.github/skills-deprecated/extract-command-from-lib/SKILL.md +0 -487
- data/.github/skills-deprecated/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +0 -22
- data/.github/skills-deprecated/extract-facade-from-base-lib/SKILL.md +0 -600
- data/.github/skills-deprecated/review-backward-compatibility/SKILL.md +0 -275
- data/.github/workflows/continuous_integration.yml +0 -358
- data/.github/workflows/enforce_conventional_commits.yml +0 -35
- data/.github/workflows/experimental_continuous_integration.yml +0 -59
- data/.github/workflows/release.yml +0 -52
- data/.github/workflows/warm_bundler_caches.yml +0 -82
- data/.gitignore +0 -30
- data/.husky/commit-msg +0 -1
- data/.husky/pre-commit +0 -13
- data/.release-please-config.json +0 -36
- data/.release-please-manifest.json +0 -3
- data/.rspec +0 -2
- data/.rubocop.yml +0 -44
- data/.rubocop_todo.yml +0 -30
- data/.yard-lint.yml +0 -75
- data/CLAUDE.md +0 -11
- data/Gemfile +0 -22
- data/Rakefile +0 -41
- data/docker/test/Dockerfile +0 -32
- data/docker/test/docker-compose.yml +0 -0
- data/package.json +0 -10
- data/redesign/1_architecture_existing.md +0 -102
- data/redesign/2_architecture_redesign.md +0 -449
- data/redesign/3_architecture_implementation.md +0 -1623
- data/redesign/Phase 4 - Step A.md +0 -366
- data/redesign/Phase 4 - Step B.md +0 -921
- data/redesign/Phase 4 - Step C.md +0 -833
- data/redesign/beta_release.md +0 -107
- data/redesign/branch_parse_refactor_plan.md +0 -163
- data/redesign/c1a-public-api-scope.tsv +0 -256
- data/redesign/c1c2_audit.md +0 -566
- data/redesign/c1c2_bucket6_lib_orphans.md +0 -626
- data/redesign/config_design.rb +0 -501
- data/redesign/index.md +0 -34
- data/redesign/info_object_migration_plan.md +0 -126
- data/redesign/integration_test_analysis.md +0 -521
- data/redesign/phase-4-step-b-test-audit.tsv +0 -485
- data/redesign/remote_refactor_plan.md +0 -164
- data/redesign/reverse_dependencies.sql +0 -44
- data/tasks/gem_tasks.rake +0 -14
- data/tasks/npm_tasks.rake +0 -7
- data/tasks/rspec.rake +0 -111
- data/tasks/rubocop.rake +0 -5
- data/tasks/test_gem.rake +0 -12
- data/tasks/yard.rake +0 -57
data/redesign/config_design.rb
DELETED
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# This is a forward looking document (in Ruby format) on the evolution of `git
|
|
4
|
-
# config` methods in Ruby Git. Here is how `git config` methods will change in
|
|
5
|
-
# v5.x and v6.x.
|
|
6
|
-
#
|
|
7
|
-
# In v5.x, `Git.config` and `Git.global_config` will continue to work as they do in
|
|
8
|
-
# v4.x but will be deprecated. New methods in the Git::Configuring module will be
|
|
9
|
-
# introduced in Git and Git::Repository to provide structured access to configuration
|
|
10
|
-
# entries. These will not be strict drop-in replacements for the existing deprecated
|
|
11
|
-
# methods.
|
|
12
|
-
#
|
|
13
|
-
# In v6.x, the deprecated methods will be removed.
|
|
14
|
-
|
|
15
|
-
# Top level Git module
|
|
16
|
-
module Git
|
|
17
|
-
# Represents a single Git configuration entry
|
|
18
|
-
#
|
|
19
|
-
# @example
|
|
20
|
-
# scope = 'local'
|
|
21
|
-
# origin = 'path/to/config'
|
|
22
|
-
# key = 'remote.origin.url'
|
|
23
|
-
# value = 'https://github.com/ruby-git/ruby-git'
|
|
24
|
-
# entry = Git::ConfigEntryInfo.new(scope:, origin:, key: , value:)
|
|
25
|
-
#
|
|
26
|
-
# @!attribute [r] scope
|
|
27
|
-
#
|
|
28
|
-
# The scope of the configuration entry
|
|
29
|
-
#
|
|
30
|
-
# May be one of "system", "global", "local", "worktree", "file", or "blob".
|
|
31
|
-
#
|
|
32
|
-
# @return [String]
|
|
33
|
-
#
|
|
34
|
-
# @!attribute [r] origin
|
|
35
|
-
#
|
|
36
|
-
# Where the configuration entry originates
|
|
37
|
-
#
|
|
38
|
-
# The origin is in the format: `<origin-type>:<actual-origin>`. It is never
|
|
39
|
-
# blank.
|
|
40
|
-
#
|
|
41
|
-
# ### `origin-type`
|
|
42
|
-
#
|
|
43
|
-
# This prefix explains the context of the configuration source. The four possible
|
|
44
|
-
# types are `file:`, `blob:`, `command line:`, and `standard input:`.
|
|
45
|
-
#
|
|
46
|
-
# ### `actual-origin`
|
|
47
|
-
#
|
|
48
|
-
# This provides the specific location for the configuration source. Only the
|
|
49
|
-
# `file:` and `blob:` origin types have an actual origin. For `command line:` and
|
|
50
|
-
# `standard input:`, Git drops this portion entirely and places the tab character
|
|
51
|
-
# immediately after the colon.
|
|
52
|
-
#
|
|
53
|
-
# #### Path Resolution
|
|
54
|
-
#
|
|
55
|
-
# When the origin type is a file, the actual origin can be formatted as either an
|
|
56
|
-
# absolute or relative path depending on how Git resolves it.
|
|
57
|
-
#
|
|
58
|
-
# - **Absolute Paths**: Git outputs the full system path for system-level configurations,
|
|
59
|
-
# global configurations, explicitly provided absolute paths, or absolute
|
|
60
|
-
# paths used in [include] directives.
|
|
61
|
-
# - **Relative Paths**: Git outputs a relative path for local repository configurations,
|
|
62
|
-
# explicitly provided relative paths, or relative paths used in [include]
|
|
63
|
-
# directives.
|
|
64
|
-
# - **Relative Anchors**: Local repository paths are relative to the repository
|
|
65
|
-
# root. Command-line relative paths are relative to your current working
|
|
66
|
-
# directory. Relative paths from an [include] directive are anchored to
|
|
67
|
-
# the parent configuration file that included them.
|
|
68
|
-
#
|
|
69
|
-
# @return [String]
|
|
70
|
-
#
|
|
71
|
-
# @!attribute [r] key
|
|
72
|
-
#
|
|
73
|
-
# The full key name of the configuration entry (e.g., remote.origin.url)
|
|
74
|
-
#
|
|
75
|
-
# @return [String]
|
|
76
|
-
#
|
|
77
|
-
# @!attribute [r] value
|
|
78
|
-
#
|
|
79
|
-
# The value of the configuration entry
|
|
80
|
-
#
|
|
81
|
-
# @return [String]
|
|
82
|
-
#
|
|
83
|
-
# @api public
|
|
84
|
-
#
|
|
85
|
-
ConfigEntryInfo = Data.define(:scope, :origin, :key, :value) do
|
|
86
|
-
# Everything up to the first dot in the {key}
|
|
87
|
-
#
|
|
88
|
-
# Returns an empty string if {key} contains no dot.
|
|
89
|
-
#
|
|
90
|
-
# @example
|
|
91
|
-
# entry.section # => 'remote'
|
|
92
|
-
#
|
|
93
|
-
# @return [String]
|
|
94
|
-
#
|
|
95
|
-
def section = first_dot ? key[0...first_dot] : ''
|
|
96
|
-
|
|
97
|
-
# Everything between the first and last dot in the {key}
|
|
98
|
-
#
|
|
99
|
-
# Returns an empty string if {key} has no subsection (zero or one dot).
|
|
100
|
-
#
|
|
101
|
-
# @example
|
|
102
|
-
# entry.subsection # => 'origin'
|
|
103
|
-
#
|
|
104
|
-
# @return [String]
|
|
105
|
-
#
|
|
106
|
-
def subsection = first_dot && first_dot != last_dot ? key[(first_dot + 1)...last_dot] : ''
|
|
107
|
-
|
|
108
|
-
# Everything after the last dot in the {key}
|
|
109
|
-
#
|
|
110
|
-
# Returns the full {key} if {key} contains no dot.
|
|
111
|
-
#
|
|
112
|
-
# @example
|
|
113
|
-
# entry.variable # => 'url'
|
|
114
|
-
#
|
|
115
|
-
# @return [String]
|
|
116
|
-
#
|
|
117
|
-
def variable = last_dot ? key[(last_dot + 1)..] : key
|
|
118
|
-
|
|
119
|
-
private
|
|
120
|
-
|
|
121
|
-
def first_dot = key.index('.')
|
|
122
|
-
|
|
123
|
-
def last_dot = key.rindex('.')
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
module Parsers
|
|
127
|
-
# Parser for `git config --get` and `git config --list` output
|
|
128
|
-
# when called with `--show-scope --show-origin --null`.
|
|
129
|
-
#
|
|
130
|
-
# @api private
|
|
131
|
-
#
|
|
132
|
-
module ConfigEntry
|
|
133
|
-
module_function
|
|
134
|
-
|
|
135
|
-
# Parse `git config --get --show-scope --show-origin --null` output.
|
|
136
|
-
#
|
|
137
|
-
# Output format (per entry): `scope\0origin\0value\0`
|
|
138
|
-
# The key name is not present in --get output; it must be supplied.
|
|
139
|
-
#
|
|
140
|
-
# @param key [String] the config key name that was queried
|
|
141
|
-
# @param output [String] raw stdout from the command
|
|
142
|
-
# @return [Git::ConfigEntryInfo, nil] the parsed entry, or nil if not found
|
|
143
|
-
#
|
|
144
|
-
def parse_get(key, output)
|
|
145
|
-
return nil if output.empty?
|
|
146
|
-
|
|
147
|
-
scope, origin, value = output.split("\0", -1)
|
|
148
|
-
Git::ConfigEntryInfo.new(scope: scope, origin: origin, key: key, value: value)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Parse `git config --get-all --show-scope --show-origin --null` output.
|
|
152
|
-
#
|
|
153
|
-
# Output format (per entry): `scope\0origin\0value\0`
|
|
154
|
-
# The key name is not present in --get-all output; it must be supplied.
|
|
155
|
-
# Entries repeat back-to-back in the same string.
|
|
156
|
-
#
|
|
157
|
-
# @param key [String] the config key name that was queried
|
|
158
|
-
# @param output [String] raw stdout from the command
|
|
159
|
-
# @return [Array<Git::ConfigEntryInfo>] the parsed entries
|
|
160
|
-
#
|
|
161
|
-
def parse_get_all(key, output)
|
|
162
|
-
return [] if output.empty?
|
|
163
|
-
|
|
164
|
-
tokens = output.split("\0", -1)
|
|
165
|
-
tokens.pop if tokens.last && tokens.last.empty?
|
|
166
|
-
tokens.each_slice(3).map do |scope, origin, value|
|
|
167
|
-
Git::ConfigEntryInfo.new(scope: scope, origin: origin, key: key, value: value)
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Parse `git config --list --show-scope --show-origin --null` output.
|
|
172
|
-
#
|
|
173
|
-
# Also used for `--get-regexp` and `--get-urlmatch` output, which share
|
|
174
|
-
# the same format.
|
|
175
|
-
#
|
|
176
|
-
# Output format (per entry): `scope\0origin\0key\nvalue\0`
|
|
177
|
-
# Entries repeat back-to-back in the same string.
|
|
178
|
-
#
|
|
179
|
-
# @param output [String] raw stdout from the command
|
|
180
|
-
# @return [Array<Git::ConfigEntryInfo>] the parsed entries
|
|
181
|
-
#
|
|
182
|
-
def parse_list(output)
|
|
183
|
-
return [] if output.empty?
|
|
184
|
-
|
|
185
|
-
tokens = output.split("\0", -1)
|
|
186
|
-
tokens.pop if tokens.last && tokens.last.empty?
|
|
187
|
-
tokens.each_slice(3).map do |scope, origin, key_value|
|
|
188
|
-
key, value = key_value.split("\n", 2)
|
|
189
|
-
Git::ConfigEntryInfo.new(scope: scope, origin: origin, key: key, value: value || '')
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Designed to be included in the Git module and the Git::Repository module
|
|
196
|
-
#
|
|
197
|
-
# The including/extending class must implement {#execution_context} and
|
|
198
|
-
# {#assert_valid_scope!}.
|
|
199
|
-
#
|
|
200
|
-
module Configuring # rubocop:disable Metrics/ModuleLength
|
|
201
|
-
# @!group Read Operations
|
|
202
|
-
|
|
203
|
-
CONFIG_GET_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type default].freeze
|
|
204
|
-
private_constant :CONFIG_GET_ALLOWED_OPTS
|
|
205
|
-
|
|
206
|
-
# @return [Git::ConfigEntryInfo, nil] nil if the key is not found
|
|
207
|
-
def config_get(name, value_regex = nil, **options)
|
|
208
|
-
Private.assert_valid_opts!(CONFIG_GET_ALLOWED_OPTS, **options)
|
|
209
|
-
assert_valid_scope!(**options)
|
|
210
|
-
options = options.merge(show_scope: true, show_origin: true, null: true)
|
|
211
|
-
cmd = Git::Commands::ConfigOptionSyntax::Get.new(execution_context)
|
|
212
|
-
output = cmd.call(name, value_regex, **options).stdout
|
|
213
|
-
Git::Parsers::ConfigEntry.parse_get(name, output)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
CONFIG_GET_ALL_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
|
|
217
|
-
private_constant :CONFIG_GET_ALL_ALLOWED_OPTS
|
|
218
|
-
|
|
219
|
-
# @return [Array<Git::ConfigEntryInfo>]
|
|
220
|
-
def config_get_all(name, value_regex = nil, **options)
|
|
221
|
-
Private.assert_valid_opts!(CONFIG_GET_ALL_ALLOWED_OPTS, **options)
|
|
222
|
-
assert_valid_scope!(**options)
|
|
223
|
-
options = options.merge(show_scope: true, show_origin: true, null: true)
|
|
224
|
-
cmd = Git::Commands::ConfigOptionSyntax::GetAll.new(execution_context)
|
|
225
|
-
output = cmd.call(name, value_regex, **options).stdout
|
|
226
|
-
Git::Parsers::ConfigEntry.parse_get_all(name, output)
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# `git config --get --type=color [--default=<default>]` is preferred over
|
|
230
|
-
# --get-color
|
|
231
|
-
#
|
|
232
|
-
# def config_get_color(name, default = nil, **)
|
|
233
|
-
# ...
|
|
234
|
-
# end
|
|
235
|
-
|
|
236
|
-
CONFIG_GET_COLORBOOL_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes].freeze
|
|
237
|
-
private_constant :CONFIG_GET_COLORBOOL_ALLOWED_OPTS
|
|
238
|
-
|
|
239
|
-
# @return [String] always 'true' or 'false'
|
|
240
|
-
def config_get_colorbool(name, stdout_is_tty = nil, **)
|
|
241
|
-
Private.assert_valid_opts!(CONFIG_GET_COLORBOOL_ALLOWED_OPTS, **)
|
|
242
|
-
assert_valid_scope!(**)
|
|
243
|
-
cmd = Git::Commands::ConfigOptionSyntax::GetColorBool.new(execution_context)
|
|
244
|
-
cmd.call(name, stdout_is_tty, **).stdout.chomp
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
CONFIG_GET_REGEXP_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
|
|
248
|
-
private_constant :CONFIG_GET_REGEXP_ALLOWED_OPTS
|
|
249
|
-
|
|
250
|
-
# @return [Array<Git::ConfigEntryInfo>]
|
|
251
|
-
def config_get_regexp(name_regex, value_regex = nil, **options)
|
|
252
|
-
Private.assert_valid_opts!(CONFIG_GET_REGEXP_ALLOWED_OPTS, **options)
|
|
253
|
-
assert_valid_scope!(**options)
|
|
254
|
-
options = options.merge(show_scope: true, show_origin: true, null: true)
|
|
255
|
-
cmd = Git::Commands::ConfigOptionSyntax::GetRegexp.new(execution_context)
|
|
256
|
-
output = cmd.call(name_regex, value_regex, **options).stdout
|
|
257
|
-
Git::Parsers::ConfigEntry.parse_list(output)
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
CONFIG_GET_URLMATCH_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
|
|
261
|
-
private_constant :CONFIG_GET_URLMATCH_ALLOWED_OPTS
|
|
262
|
-
|
|
263
|
-
# @return [Array<Git::ConfigEntryInfo>]
|
|
264
|
-
def config_get_urlmatch(name, url, **options)
|
|
265
|
-
Private.assert_valid_opts!(CONFIG_GET_URLMATCH_ALLOWED_OPTS, **options)
|
|
266
|
-
assert_valid_scope!(**options)
|
|
267
|
-
options = options.merge(show_scope: true, show_origin: true, null: true)
|
|
268
|
-
cmd = Git::Commands::ConfigOptionSyntax::GetUrlmatch.new(execution_context)
|
|
269
|
-
output = cmd.call(name, url, **options).stdout
|
|
270
|
-
Git::Parsers::ConfigEntry.parse_list(output)
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
CONFIG_LIST_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
|
|
274
|
-
private_constant :CONFIG_LIST_ALLOWED_OPTS
|
|
275
|
-
|
|
276
|
-
# @return [Array<Git::ConfigEntryInfo>]
|
|
277
|
-
def config_list(**options)
|
|
278
|
-
Private.assert_valid_opts!(CONFIG_LIST_ALLOWED_OPTS, **options)
|
|
279
|
-
assert_valid_scope!(**options)
|
|
280
|
-
options = options.merge(show_scope: true, show_origin: true, null: true)
|
|
281
|
-
cmd = Git::Commands::ConfigOptionSyntax::List.new(execution_context)
|
|
282
|
-
output = cmd.call(**options).stdout
|
|
283
|
-
Git::Parsers::ConfigEntry.parse_list(output)
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
# @!endgroup
|
|
287
|
-
|
|
288
|
-
# @!group Write Operations
|
|
289
|
-
|
|
290
|
-
CONFIG_ADD_ALLOWED_OPTS = %i[global system local worktree file f blob type].freeze
|
|
291
|
-
private_constant :CONFIG_ADD_ALLOWED_OPTS
|
|
292
|
-
|
|
293
|
-
# @return [void]
|
|
294
|
-
#
|
|
295
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
296
|
-
#
|
|
297
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
298
|
-
def config_add(name, value, **)
|
|
299
|
-
Private.assert_valid_opts!(CONFIG_ADD_ALLOWED_OPTS, **)
|
|
300
|
-
assert_valid_scope!(**)
|
|
301
|
-
cmd = Git::Commands::ConfigOptionSyntax::Add.new(execution_context)
|
|
302
|
-
cmd.call(name, value, **)
|
|
303
|
-
nil
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
CONFIG_REMOVE_SECTION_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
|
|
307
|
-
private_constant :CONFIG_REMOVE_SECTION_ALLOWED_OPTS
|
|
308
|
-
|
|
309
|
-
# @return [void]
|
|
310
|
-
#
|
|
311
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
312
|
-
#
|
|
313
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
314
|
-
def config_remove_section(name, **)
|
|
315
|
-
Private.assert_valid_opts!(CONFIG_REMOVE_SECTION_ALLOWED_OPTS, **)
|
|
316
|
-
assert_valid_scope!(**)
|
|
317
|
-
cmd = Git::Commands::ConfigOptionSyntax::RemoveSection.new(execution_context)
|
|
318
|
-
cmd.call(name, **)
|
|
319
|
-
nil
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
CONFIG_RENAME_SECTION_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
|
|
323
|
-
private_constant :CONFIG_RENAME_SECTION_ALLOWED_OPTS
|
|
324
|
-
|
|
325
|
-
# @return [void]
|
|
326
|
-
#
|
|
327
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
328
|
-
#
|
|
329
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
330
|
-
def config_rename_section(old_name, new_name, **)
|
|
331
|
-
Private.assert_valid_opts!(CONFIG_RENAME_SECTION_ALLOWED_OPTS, **)
|
|
332
|
-
assert_valid_scope!(**)
|
|
333
|
-
cmd = Git::Commands::ConfigOptionSyntax::RenameSection.new(execution_context)
|
|
334
|
-
cmd.call(old_name, new_name, **)
|
|
335
|
-
nil
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
CONFIG_REPLACE_ALL_ALLOWED_OPTS = %i[global system local worktree file f blob type].freeze
|
|
339
|
-
private_constant :CONFIG_REPLACE_ALL_ALLOWED_OPTS
|
|
340
|
-
|
|
341
|
-
# @return [void]
|
|
342
|
-
#
|
|
343
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
344
|
-
#
|
|
345
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
346
|
-
def config_replace_all(name, value, value_regex = nil, **)
|
|
347
|
-
Private.assert_valid_opts!(CONFIG_REPLACE_ALL_ALLOWED_OPTS, **)
|
|
348
|
-
assert_valid_scope!(**)
|
|
349
|
-
cmd = Git::Commands::ConfigOptionSyntax::ReplaceAll.new(execution_context)
|
|
350
|
-
cmd.call(name, value, value_regex, **)
|
|
351
|
-
nil
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
CONFIG_SET_ALLOWED_OPTS = %i[global system local worktree file f blob type].freeze
|
|
355
|
-
private_constant :CONFIG_SET_ALLOWED_OPTS
|
|
356
|
-
|
|
357
|
-
# @return [void]
|
|
358
|
-
#
|
|
359
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
360
|
-
#
|
|
361
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
362
|
-
def config_set(name, value, **)
|
|
363
|
-
Private.assert_valid_opts!(CONFIG_SET_ALLOWED_OPTS, **)
|
|
364
|
-
assert_valid_scope!(**)
|
|
365
|
-
cmd = Git::Commands::ConfigOptionSyntax::Set.new(execution_context)
|
|
366
|
-
cmd.call(name, value, **)
|
|
367
|
-
nil
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
CONFIG_UNSET_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
|
|
371
|
-
private_constant :CONFIG_UNSET_ALLOWED_OPTS
|
|
372
|
-
|
|
373
|
-
# @return [void]
|
|
374
|
-
#
|
|
375
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
376
|
-
#
|
|
377
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
378
|
-
def config_unset(name, value_regex = nil, **)
|
|
379
|
-
Private.assert_valid_opts!(CONFIG_UNSET_ALLOWED_OPTS, **)
|
|
380
|
-
assert_valid_scope!(**)
|
|
381
|
-
cmd = Git::Commands::ConfigOptionSyntax::Unset.new(execution_context)
|
|
382
|
-
cmd.call(name, value_regex, **)
|
|
383
|
-
nil
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
CONFIG_UNSET_ALL_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
|
|
387
|
-
private_constant :CONFIG_UNSET_ALL_ALLOWED_OPTS
|
|
388
|
-
|
|
389
|
-
# @return [void]
|
|
390
|
-
#
|
|
391
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
392
|
-
#
|
|
393
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
394
|
-
def config_unset_all(name, value_regex = nil, **)
|
|
395
|
-
Private.assert_valid_opts!(CONFIG_UNSET_ALL_ALLOWED_OPTS, **)
|
|
396
|
-
assert_valid_scope!(**)
|
|
397
|
-
cmd = Git::Commands::ConfigOptionSyntax::UnsetAll.new(execution_context)
|
|
398
|
-
cmd.call(name, value_regex, **)
|
|
399
|
-
nil
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
# @!endgroup
|
|
403
|
-
|
|
404
|
-
private
|
|
405
|
-
|
|
406
|
-
# @abstract Including/extending class must implement execution_context
|
|
407
|
-
#
|
|
408
|
-
# @return [Git::ExecutionContext]
|
|
409
|
-
#
|
|
410
|
-
def execution_context
|
|
411
|
-
raise NotImplementedError
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
# @abstract Including/extending class must implement assert_valid_scope!
|
|
415
|
-
#
|
|
416
|
-
# Called before every config operation to validate that the requested scope
|
|
417
|
-
# is appropriate for the context (e.g., repository-specific scopes such as
|
|
418
|
-
# `local` are not valid when called without a repository).
|
|
419
|
-
#
|
|
420
|
-
# @raise [ArgumentError] if the scope is not permitted in this context
|
|
421
|
-
#
|
|
422
|
-
# @return [void]
|
|
423
|
-
#
|
|
424
|
-
def assert_valid_scope!(**)
|
|
425
|
-
raise NotImplementedError
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
# Internal helpers local to {Git::Configuring}
|
|
429
|
-
#
|
|
430
|
-
# @api private
|
|
431
|
-
#
|
|
432
|
-
module Private
|
|
433
|
-
module_function
|
|
434
|
-
|
|
435
|
-
# Validate that `options` contains only keys listed in `allowed`
|
|
436
|
-
#
|
|
437
|
-
# @example Reject an undocumented option
|
|
438
|
-
# Private.assert_valid_opts!(%i[all force], bogus: true)
|
|
439
|
-
# #=> raises ArgumentError: Unknown options: bogus
|
|
440
|
-
#
|
|
441
|
-
# @param allowed [Array<Symbol>] the keys permitted by the facade method
|
|
442
|
-
#
|
|
443
|
-
# @param options [Hash] the options hash provided by the caller
|
|
444
|
-
#
|
|
445
|
-
# @return [void]
|
|
446
|
-
#
|
|
447
|
-
# @raise [ArgumentError] when `options` contains any key not in `allowed`
|
|
448
|
-
#
|
|
449
|
-
def assert_valid_opts!(allowed, **options)
|
|
450
|
-
unknown = options.keys - allowed
|
|
451
|
-
return if unknown.empty?
|
|
452
|
-
|
|
453
|
-
raise ArgumentError, "Unknown options: #{unknown.join(', ')}"
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
private_constant :Private
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
REPOSITORY_SPECIFIC_SCOPES = %i[local worktree blob].freeze
|
|
460
|
-
private_constant :REPOSITORY_SPECIFIC_SCOPES
|
|
461
|
-
|
|
462
|
-
# Reopens Git::Repository to mix in config read/write operations.
|
|
463
|
-
#
|
|
464
|
-
# In the real implementation this `include` lives in
|
|
465
|
-
# `lib/git/repository/configuring.rb` and is picked up by the
|
|
466
|
-
# `Git::Repository` class definition in `lib/git/repository.rb`.
|
|
467
|
-
class Repository
|
|
468
|
-
# Mixes in config_get, config_list, config_set, etc.
|
|
469
|
-
include Git::Configuring
|
|
470
|
-
|
|
471
|
-
private
|
|
472
|
-
|
|
473
|
-
# @return [Git::ExecutionContext::Repository]
|
|
474
|
-
attr_reader :execution_context
|
|
475
|
-
|
|
476
|
-
# All scopes are permitted when called with a repository context.
|
|
477
|
-
def assert_valid_scope!(**)
|
|
478
|
-
# All scopes allowed
|
|
479
|
-
end
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
# Enables calling Git.config_get('user.name')
|
|
483
|
-
extend Git::Configuring
|
|
484
|
-
|
|
485
|
-
# Returns a global execution context for non-repository config operations.
|
|
486
|
-
#
|
|
487
|
-
# Overrides the abstract {Git::Configuring#execution_context} for the
|
|
488
|
-
# module-level case.
|
|
489
|
-
#
|
|
490
|
-
# @return [Git::ExecutionContext::Global]
|
|
491
|
-
def self.execution_context
|
|
492
|
-
Git::ExecutionContext::Global.new
|
|
493
|
-
end
|
|
494
|
-
private_class_method :execution_context
|
|
495
|
-
|
|
496
|
-
def self.assert_valid_scope!(**options)
|
|
497
|
-
repo_scopes = options.keys.select { |k| options[k] && REPOSITORY_SPECIFIC_SCOPES.include?(k) }
|
|
498
|
-
raise ArgumentError, "scope #{repo_scopes.first} requires a repository" if repo_scopes.any?
|
|
499
|
-
end
|
|
500
|
-
private_class_method :assert_valid_scope!
|
|
501
|
-
end
|
data/redesign/index.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Architectural Redesign Project
|
|
2
|
-
|
|
3
|
-
[This project was announced in the project's README](../README.md#2025-07-09-architectural-redesign)
|
|
4
|
-
|
|
5
|
-
The git gem is undergoing a significant architectural redesign for the upcoming
|
|
6
|
-
v5.0.0 release. The current architecture has several design challenges that make it
|
|
7
|
-
difficult to maintain and evolve. This redesign aims to address these issues by
|
|
8
|
-
introducing a clearer, more robust, and more testable structure.
|
|
9
|
-
|
|
10
|
-
We have prepared detailed documents outlining the analysis of the current
|
|
11
|
-
architecture and the proposed changes. We encourage our community and contributors to
|
|
12
|
-
review them:
|
|
13
|
-
|
|
14
|
-
1. [Analysis of the Current Architecture](1_architecture_existing.md): A
|
|
15
|
-
breakdown of the existing design and its challenges.
|
|
16
|
-
2. [The Proposed Redesign](2_architecture_redesign.md): An overview of the
|
|
17
|
-
new three-layered architecture.
|
|
18
|
-
3. [Implementation Plan](3_architecture_implementation.md): The step-by-step
|
|
19
|
-
plan for implementing the redesign.
|
|
20
|
-
|
|
21
|
-
Your feedback is welcome! Please feel free to open an issue to discuss the proposed
|
|
22
|
-
changes.
|
|
23
|
-
|
|
24
|
-
> **DON'T PANIC!**
|
|
25
|
-
>
|
|
26
|
-
> While this is a major internal refactoring, our goal is to keep the primary public
|
|
27
|
-
API on the main repository object as stable as possible. Most users who rely on
|
|
28
|
-
documented methods like `g.commit`, `g.add`, and `g.status` should find the
|
|
29
|
-
transition to v5.0.0 straightforward.
|
|
30
|
-
>
|
|
31
|
-
> The breaking changes will primarily affect users who have been relying on the
|
|
32
|
-
internal g.lib accessor, which will be removed as part of this cleanup. For more
|
|
33
|
-
details, please see the "Impact on Users" section in [the redesign
|
|
34
|
-
> document](2_architecture_redesign.md).
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
# Plan: Migrate ActiveRecord-style classes to `*Info` value objects
|
|
2
|
-
|
|
3
|
-
This document is the durable summary and issue index for the effort to migrate
|
|
4
|
-
ruby-git's "ActiveRecord-style" domain classes (rich objects constructed with
|
|
5
|
-
`def initialize(base, ...)` that both hold data **and** perform git operations)
|
|
6
|
-
toward immutable `*Info` value objects (DAOs), following the pattern established
|
|
7
|
-
by `Git::BranchInfo` and `Git::RemoteInfo`.
|
|
8
|
-
|
|
9
|
-
It captures analysis and decisions so they are not re-derived in future sessions.
|
|
10
|
-
It is a planning/index document, not a specification — each tracked issue owns the
|
|
11
|
-
detailed design for its area.
|
|
12
|
-
|
|
13
|
-
## Contents
|
|
14
|
-
|
|
15
|
-
- [Motivation](#motivation)
|
|
16
|
-
- [The pattern](#the-pattern)
|
|
17
|
-
- [Status of each class](#status-of-each-class)
|
|
18
|
-
- [Issue index](#issue-index)
|
|
19
|
-
- [Versioning and sequencing](#versioning-and-sequencing)
|
|
20
|
-
- [Key decisions](#key-decisions)
|
|
21
|
-
- [Open threads / not yet tracked](#open-threads--not-yet-tracked)
|
|
22
|
-
|
|
23
|
-
## Motivation
|
|
24
|
-
|
|
25
|
-
The modern facade design returns immutable, parser-built value objects
|
|
26
|
-
(`Git::BranchInfo`, `Git::RemoteInfo`, `Git::StashInfo`, `Git::TagInfo`,
|
|
27
|
-
`Git::ConfigEntryInfo`, the `Diff*Info` family, …). Several older classes predate
|
|
28
|
-
this and mix data with operations while holding a repository reference (`@base`).
|
|
29
|
-
They are harder to test, encourage stateful usage, and (in some cases) carry
|
|
30
|
-
latent bugs. Migrating them to `*Info` value objects plus name-based facade
|
|
31
|
-
operations aligns the whole API and unblocks cleaner deprecations.
|
|
32
|
-
|
|
33
|
-
The recurring "tell" for a migration candidate is a `def initialize(base, ...)`
|
|
34
|
-
constructor combined with operation methods — the object both *is* data and
|
|
35
|
-
*does* work.
|
|
36
|
-
|
|
37
|
-
## The pattern
|
|
38
|
-
|
|
39
|
-
For each domain:
|
|
40
|
-
|
|
41
|
-
1. **Value object** — an immutable `Data.define` `*Info` at the top-level `Git::`
|
|
42
|
-
namespace holding only parsed metadata, with `#to_s` returning the string a
|
|
43
|
-
caller can pass back to git-facing methods (e.g. a refname or stash selector).
|
|
44
|
-
2. **Parser** — a `Git::Parsers::*` that builds `Array<*Info>` from raw git output.
|
|
45
|
-
3. **Facade** — `Git::Repository::*` methods that return the value objects and that
|
|
46
|
-
accept names / `*Info` / any `#to_s` for operations.
|
|
47
|
-
4. **Deprecation** — the legacy AR class, its collection, and the facade methods
|
|
48
|
-
that return them are deprecated (additive, non-breaking) in one major and
|
|
49
|
-
removed in the next.
|
|
50
|
-
|
|
51
|
-
## Status of each class
|
|
52
|
-
|
|
53
|
-
| Domain | AR class(es) | Value object | State |
|
|
54
|
-
| --- | --- | --- | --- |
|
|
55
|
-
| Branch | `Git::Branch` / `Git::Branches` | `Git::BranchInfo`, `Git::DetachedHeadInfo` | Value object + `branch_list` shipped; deprecation of AR classes tracked (#1639) |
|
|
56
|
-
| Remote | `Git::Remote` | `Git::RemoteInfo` | `RemoteInfo` + parser shipped; `remotes` deprecation tracked (#1640); `Git::Remote` class deprecation tracked (#1643) |
|
|
57
|
-
| Stash | `Git::Stash` / `Git::Stashes` | `Git::StashInfo` (exists) | Redesign tracked (#1634); `Git::Branch#stashes` cleanup (#1637) |
|
|
58
|
-
| Worktree | `Git::Worktree` / `Git::Worktrees` | *(needs `Git::WorktreeInfo`)* | Redesign tracked (#1635) — value object + parser do not exist yet |
|
|
59
|
-
| Tag | `Git::Object::Tag` | `Git::TagInfo` (exists) | Half-migrated; finish tracked under umbrella (#1636) |
|
|
60
|
-
| Commit / Tree / Blob | `Git::Object::Commit/Tree/Blob` | *(none)* | Deferred, umbrella (#1636) |
|
|
61
|
-
| Status | `Git::Status` / `Git::Status::StatusFile` | *(none)* | Deferred, umbrella (#1636) |
|
|
62
|
-
| Author | `Git::Author` (mutable) | *(none)* | Deferred, umbrella (#1636) |
|
|
63
|
-
| Diff / Log | `Git::Diff`, `Git::Log` | many `Diff*Info`, `Git::Log::Result` | Value layer largely exists; return-type audit deferred (#1636) |
|
|
64
|
-
|
|
65
|
-
## Issue index
|
|
66
|
-
|
|
67
|
-
- **#1631** — bug: slash-containing remote names in the `Git::Branch` string
|
|
68
|
-
constructor path. Kept open; resolution is to route users to the `BranchInfo`
|
|
69
|
-
path (folds into #1639).
|
|
70
|
-
- **#1634** — redesign `Git::Repository::Stashing` around `Git::StashInfo`.
|
|
71
|
-
- **#1635** — redesign `Git::Repository::WorktreeOperations` around a new
|
|
72
|
-
`Git::WorktreeInfo` (needs value object + parser first).
|
|
73
|
-
- **#1636** — umbrella tracker for post-7.x AR→`*Info` migrations
|
|
74
|
-
(Commit/Tree/Blob, finish Tag, Status, Author, Diff/Log audit).
|
|
75
|
-
- **#1637** — deprecate `Git::Branch#stashes` (ignores the branch, returns all
|
|
76
|
-
repo stashes) in favor of `Git::Repository#stashes_all`.
|
|
77
|
-
- **#1639** — deprecate `Git::Branch` / `Git::Branches` and
|
|
78
|
-
`Git::Repository#branch` / `#branches` in favor of the `BranchInfo` API.
|
|
79
|
-
- **#1640** — deprecate `Git::Repository#remotes` in favor of `#remote_list`
|
|
80
|
-
(from `remote_refactor_plan.md` PR 3, Step 5).
|
|
81
|
-
- **#1641** — add `Git::Repository#in_branch` and a merge-into-branch facade path
|
|
82
|
-
(preconditions for #1639).
|
|
83
|
-
- **#1643** — deprecate `Git::Remote` (and `Git::Repository#remote`) in favor of
|
|
84
|
-
the `RemoteInfo` API (remote analog of #1639; depends on #1640).
|
|
85
|
-
|
|
86
|
-
## Versioning and sequencing
|
|
87
|
-
|
|
88
|
-
- Each migration is **additive + deprecate** in one major, **remove** in the next
|
|
89
|
-
(e.g. deprecate in 5.x → remove in 6.x, or the analogous later pair).
|
|
90
|
-
- Stash (#1634) and Worktree (#1635) are the in-flight redesigns; the remaining
|
|
91
|
-
candidates in #1636 are intentionally deferred until after the 7.x cycle.
|
|
92
|
-
- **Guardrail during any major:** do not add *new* public APIs that return
|
|
93
|
-
AR-style objects that are already destined for deprecation. When touching these
|
|
94
|
-
areas, prefer adding the value-object-returning method. The `Git::Object::Tag`
|
|
95
|
-
vs `Git::TagInfo` split is the most important to watch (already half-migrated).
|
|
96
|
-
|
|
97
|
-
## Key decisions
|
|
98
|
-
|
|
99
|
-
- **`Git::WorktreeInfo` should capture the full porcelain record** — `path`,
|
|
100
|
-
`head`, `branch`, `bare`, `detached`, `locked` (+reason), `prunable` (+reason) —
|
|
101
|
-
not just the `[dir, sha]` tuple `worktrees_all` returns today.
|
|
102
|
-
- **Branch has no capability gap.** An audit confirmed every `Git::Branch`
|
|
103
|
-
operation already delegates to an existing `Git::Repository` facade method that
|
|
104
|
-
accepts a name/string. The only missing pieces are two composites — `in_branch`
|
|
105
|
-
and the `merge(branch)` overload — tracked in #1641 as deprecation preconditions.
|
|
106
|
-
- **`checkout` semantic wrinkle:** `Git::Branch#checkout` auto-creates the branch
|
|
107
|
-
first; `Git::Repository#checkout(name)` does not. Deprecation notes must call
|
|
108
|
-
this out.
|
|
109
|
-
- **`Git::Branch#stashes` is effectively a latent bug** — it ignores the branch
|
|
110
|
-
receiver and returns all repository stashes — so it is "deprecate and delete,"
|
|
111
|
-
not "migrate" (#1637).
|
|
112
|
-
- **`Git::Repository::Branching` / `RemoteOperations` / `WorktreeOperations` are
|
|
113
|
-
the replacements, not deprecation targets.** Only the specific methods that
|
|
114
|
-
return AR objects (`#branch`, `#branches`, `#remotes`, `#worktree`,
|
|
115
|
-
`#worktrees`) are deprecated; the operation methods stay.
|
|
116
|
-
- **Stashes are not branch-scoped in git**, so no branch-scoped stash API is
|
|
117
|
-
warranted.
|
|
118
|
-
|
|
119
|
-
## Open threads
|
|
120
|
-
|
|
121
|
-
All previously loose threads are now tracked within the issues above:
|
|
122
|
-
|
|
123
|
-
- **`Git::Repository#worktree` / `#worktrees` factory deprecation** — in scope of
|
|
124
|
-
#1635 (confirmed in its transition plan).
|
|
125
|
-
- **`config_remote` relationship to `remote_list`** — folded into #1640 as an open
|
|
126
|
-
question to resolve during that work.
|