Skip to content

Commit 12b2e59

Browse files
jcouballCopilot
andcommitted
feat: add Git::Repository::Configuring#global_config facade and deprecated aliases
Implements the global_config(name = nil, value = nil) method on Git::Repository::Configuring with the same three-way dispatch as config(): - global_config() => Hash of all global config entries - global_config(name) => String value of one entry - global_config(name, value) => Git::CommandLineResult Each mode is backed by a new private helper in the Private module (global_config_get, global_config_list, global_config_set) that passes global: true to the existing ConfigOptionSyntax command classes. Also adds three deprecated forwarding methods for backward compatibility: - global_config_get(name) - delegates to global_config(name) - global_config_list - delegates to global_config - global_config_set(name, value) - delegates to global_config(name, value) Each deprecated method emits Git::Deprecation.warn and delegates to global_config; they will be removed in v6. Git::Base delegators are added for all four methods. Closes redesign/c1c2_bucket6_lib_orphans.md §3.3. Updates c1c2_audit.md §7.3 to mark the three global_config_* rows as promoted, and decrements the human-decision count from 16 to 13. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9e532bd commit 12b2e59

7 files changed

Lines changed: 421 additions & 8 deletions

File tree

lib/git/base.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,22 @@ def config(name = nil, value = nil, options = {})
231231
facade_repository.config(name, value, options)
232232
end
233233

234+
def global_config(name = nil, value = nil)
235+
facade_repository.global_config(name, value)
236+
end
237+
238+
def global_config_get(name)
239+
facade_repository.global_config_get(name)
240+
end
241+
242+
def global_config_list
243+
facade_repository.global_config_list
244+
end
245+
246+
def global_config_set(name, value)
247+
facade_repository.global_config_set(name, value)
248+
end
249+
234250
# Returns a reference to the working directory
235251
#
236252
# @example

lib/git/repository/configuring.rb

Lines changed: 140 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ module Git
77
class Repository
88
# Facade methods for reading and writing git configuration
99
#
10-
# Provides the {#config} method, which dispatches to read a single entry,
11-
# list all entries, or write a value depending on the arguments supplied.
10+
# Provides the {#config} and {#global_config} methods, which dispatch to read a
11+
# single entry, list all entries, or write a value depending on the arguments
12+
# supplied. {#config} uses git's default config scope (reads from the full
13+
# resolution chain; set operations write to the repository's `.git/config`);
14+
# {#global_config} targets the git global config scope (`git config --global`).
1215
#
1316
# Included by {Git::Repository}.
1417
#
@@ -81,6 +84,89 @@ def config(name = nil, value = nil, options = {})
8184
end
8285
end
8386

87+
# Read or write a global git configuration entry
88+
#
89+
# Dispatches to one of three modes depending on the arguments supplied,
90+
# targeting the git global config scope (`git config --global`):
91+
#
92+
# * **List** — `global_config()` returns all global config entries as a `Hash`.
93+
# * **Get** — `global_config(name)` returns the value for a single key as a `String`.
94+
# * **Set** — `global_config(name, value)` writes a value and returns the raw
95+
# command result.
96+
#
97+
# @overload global_config
98+
#
99+
# @example List all global config entries
100+
# repo.global_config #=> { "user.name" => "Alice", "core.autocrlf" => "false" }
101+
#
102+
# @return [Hash{String => String}] all global config entries, keyed by their
103+
# full dotted key names (e.g. `"user.name"`)
104+
#
105+
# @raise [Git::FailedError] if git exits with a non-zero exit status
106+
#
107+
# @overload global_config(name)
108+
#
109+
# @example Read the global committer name
110+
# repo.global_config('user.name') #=> "Alice"
111+
#
112+
# @param name [String] the dotted config key to look up (e.g. `"user.name"`)
113+
#
114+
# @return [String] the value of the global config entry
115+
#
116+
# @raise [Git::FailedError] if git exits with a non-zero exit status
117+
#
118+
# @overload global_config(name, value)
119+
#
120+
# @example Set the global committer name
121+
# repo.global_config('user.name', 'Alice')
122+
#
123+
# @param name [String] the dotted config key to write (e.g. `"user.name"`)
124+
#
125+
# @param value [#to_s] the value to assign; any object is accepted and
126+
# converted to a String via `#to_s` before being passed to git
127+
#
128+
# @return [Git::CommandLineResult] the raw result of
129+
# `git config --global <name> <value>`
130+
#
131+
# @raise [Git::FailedError] if git exits with a non-zero exit status
132+
#
133+
def global_config(name = nil, value = nil)
134+
if !name.nil? && !value.nil?
135+
Private.global_config_set(@execution_context, name, value)
136+
elsif !name.nil?
137+
Private.global_config_get(@execution_context, name)
138+
else
139+
Private.global_config_list(@execution_context)
140+
end
141+
end
142+
143+
# @deprecated Use {#global_config} instead.
144+
def global_config_get(name)
145+
Git::Deprecation.warn(
146+
'Git::Repository#global_config_get is deprecated and will be removed in a future version. ' \
147+
'Use global_config(name) instead.'
148+
)
149+
global_config(name)
150+
end
151+
152+
# @deprecated Use {#global_config} instead.
153+
def global_config_list
154+
Git::Deprecation.warn(
155+
'Git::Repository#global_config_list is deprecated and will be removed in a future version. ' \
156+
'Use global_config instead.'
157+
)
158+
global_config
159+
end
160+
161+
# @deprecated Use {#global_config} instead.
162+
def global_config_set(name, value)
163+
Git::Deprecation.warn(
164+
'Git::Repository#global_config_set is deprecated and will be removed in a future version. ' \
165+
'Use global_config(name, value) instead.'
166+
)
167+
global_config(name, value)
168+
end
169+
84170
# Private helpers local to {Git::Repository::Configuring}
85171
#
86172
# @api private
@@ -148,6 +234,58 @@ def config_list(execution_context)
148234
hsh[key] = value || ''
149235
end
150236
end
237+
238+
# Retrieve a global config value by key name
239+
#
240+
# @param execution_context [Git::ExecutionContext] the execution context
241+
#
242+
# @param name [String] the dotted config key to look up (e.g. `"user.name"`)
243+
#
244+
# @return [String] the value of the global config entry
245+
#
246+
# @raise [Git::FailedError] if git exits with a non-zero exit status
247+
#
248+
def global_config_get(execution_context, name)
249+
result = Git::Commands::ConfigOptionSyntax::Get.new(execution_context).call(name, global: true)
250+
raise Git::FailedError, result if result.status.exitstatus != 0
251+
252+
result.stdout
253+
end
254+
255+
# Retrieve all global config entries as a hash
256+
#
257+
# @param execution_context [Git::ExecutionContext] the execution context
258+
#
259+
# @return [Hash{String => String}] all global config entries, keyed by their full
260+
# dotted key names (e.g. `"user.name"`)
261+
#
262+
# @raise [Git::FailedError] if git exits with a non-zero exit status
263+
#
264+
def global_config_list(execution_context)
265+
lines = Git::Commands::ConfigOptionSyntax::List.new(execution_context).call(global: true).stdout.split("\n")
266+
lines.each_with_object({}) do |line, hsh|
267+
key, value = line.split('=', 2)
268+
hsh[key] = value || ''
269+
end
270+
end
271+
272+
# Set a global config value by key name
273+
#
274+
# @param execution_context [Git::ExecutionContext] the execution context
275+
#
276+
# @param name [String] the dotted config key to write (e.g. `"user.name"`)
277+
#
278+
# @param value [#to_s] the value to assign; any object is accepted and
279+
# converted to a String via `#to_s` before being passed to git
280+
#
281+
# @return [Git::CommandLineResult] the raw result of
282+
# `git config --global <name> <value>`
283+
#
284+
# @raise [Git::FailedError] if git exits with a non-zero exit status
285+
#
286+
def global_config_set(execution_context, name, value)
287+
Git::Commands::ConfigOptionSyntax::Set.new(execution_context).call(name, value, global: true)
288+
end
151289
end
152290

153291
private_constant :Private

redesign/c1c2_audit.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,9 @@ These require a new facade method before a base.rb delegator can be added.
401401
| `config_get(name)` | Returns a single config value. Used by tooling. Part of the existing `config()` facade which reads/writes. | 🔍 human decision — expose as `config_get` or fold into `config(name)`? |
402402
| `config_list` | Returns full config hash. Used by tooling. | 🔍 human decision — expose separately or fold into `config()`? |
403403
| `config_set(name, value, options)` | Sets a config value. | 🔍 human decision — expose separately or fold into `config(name, value)`? |
404-
| `global_config_get(name)` | Gets a global config value. | 🔍 human decision |
405-
| `global_config_list` | Returns the full global config hash. | 🔍 human decision |
406-
| `global_config_set(name, value)` | Sets a global config value. | 🔍 human decision |
404+
| `global_config_get(name)` | Gets a global config value. | ✅ promoted — `global_config` facade + deprecated aliases in `Git::Repository::Configuring` + `Git::Base` delegators added (PR 5h-3) |
405+
| `global_config_list` | Returns the full global config hash. | ✅ promoted — `global_config` facade + deprecated aliases in `Git::Repository::Configuring` + `Git::Base` delegators added (PR 5h-3) |
406+
| `global_config_set(name, value)` | Sets a global config value. | ✅ promoted — `global_config` facade + deprecated aliases in `Git::Repository::Configuring` + `Git::Base` delegators added (PR 5h-3) |
407407
| `git_version` | Returns `Git::Version` for the current binary. Useful for tooling that conditionally enables features. Not a repository concern; `Git.git_version` is the canonical API. | ✅ delegator added to `Git::Base` — delegates to `Git.git_version` |
408408
| `list_files(ref_dir)` | Lists files under `.git/refs/{ref_dir}`. Internal ref-filesystem access. No plausible clean public use. | ❌ remove — internal plumbing; direct callers should migrate to `Git::Repository` ref-inspection methods |
409409
| `ls_remote(location = nil, opts = {})` | Lists remote refs. Clearly useful externally. | ✅ promoted — facade in `Git::Repository::RemoteOperations` + `Git::Base` delegator added (PR 5f) |
@@ -438,14 +438,14 @@ upgrade notes as "unsupported; remove any `g.lib.X` calls."
438438

439439
| Status | Count |
440440
|--------|-------|
441-
| ✅ promote (repo already had it, `Git::Base` delegator added — PR 2d; or alias added) | 26 |
441+
| ✅ promote (repo already had it, `Git::Base` delegator added — PR 2d; or alias added) | 29 |
442442
| ⬜ promote (new facade work required) | 1 |
443443
| ❌ remove (internal plumbing) | 12 |
444-
| 🔍 human decision | 14 |
444+
| 🔍 human decision | 11 |
445445
| **Total orphaned methods** | **56** |
446446

447447
> **Recommendation:** The 24 "trivial wiring" promotions can be handled in PR 5a
448-
> as a batch. The 4 "new facade" promotions and 16 human-decision items should be
448+
> as a batch. The 1 "new facade" promotion and 11 human-decision items should be
449449
> addressed in a companion document (`redesign/c1c2_bucket6_lib_orphans.md`)
450450
> before PR 5b begins.
451451

redesign/c1c2_bucket6_lib_orphans.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ These can be removed in v6 after a full deprecation cycle.
326326

327327
**Decision:** Accepted. Add `global_config(name = nil, value = nil)` to `Git::Repository::Configuring` with three-way dispatch (list/get/set). Add deprecated forwarding methods `global_config_get`, `global_config_list`, `global_config_set` with `Git::Deprecation.warn` calls. Add `Git::Base` delegators for all four. Remove deprecated aliases in v6.
328328

329+
**Implemented in PR 5h-3.** `global_config` facade added to `Git::Repository::Configuring` with three-way dispatch; private helpers `global_config_get/list/set` in the `Private` module; deprecated forwarding methods on the public API; `Git::Base` delegators for all four methods.
330+
329331
---
330332

331333
### 3.4 `parse_config(file)`

spec/integration/git/repository/configuring_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,41 @@
4545
end
4646
end
4747
end
48+
49+
describe '#global_config' do
50+
around do |example|
51+
with_isolated_global_config { example.run }
52+
end
53+
54+
context 'when called with no arguments' do
55+
before do
56+
described_instance.global_config('user.name', 'GlobalUser')
57+
described_instance.global_config('user.email', 'global@example.com')
58+
end
59+
60+
it 'returns a Hash containing the written global config entries' do
61+
result = described_instance.global_config
62+
expect(result).to be_a(Hash)
63+
expect(result).to include('user.name' => 'GlobalUser', 'user.email' => 'global@example.com')
64+
end
65+
end
66+
67+
context 'when called with a name' do
68+
before { described_instance.global_config('user.name', 'GlobalUser') }
69+
70+
it 'returns the String value for the named key from global config' do
71+
expect(described_instance.global_config('user.name')).to eq('GlobalUser')
72+
end
73+
end
74+
75+
def with_isolated_global_config
76+
global_config = File.join(repo_dir, 'global.config')
77+
FileUtils.touch(global_config)
78+
saved = ENV.fetch('GIT_CONFIG_GLOBAL', nil)
79+
ENV['GIT_CONFIG_GLOBAL'] = global_config
80+
yield
81+
ensure
82+
saved.nil? ? ENV.delete('GIT_CONFIG_GLOBAL') : ENV['GIT_CONFIG_GLOBAL'] = saved
83+
end
84+
end
4885
end

spec/unit/git/base_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,4 +460,42 @@
460460
expect(described_instance.stash_list).to eq('stash@{0}: WIP')
461461
end
462462
end
463+
464+
describe '#global_config' do
465+
include_context 'with a stubbed facade_repository'
466+
467+
it 'delegates to facade_repository.global_config with name and value' do
468+
expect(facade_repository).to receive(:global_config).with('user.name', 'Alice').and_return(nil)
469+
described_instance.global_config('user.name', 'Alice')
470+
end
471+
end
472+
473+
describe '#global_config_get' do
474+
include_context 'with a stubbed facade_repository'
475+
476+
it 'delegates to facade_repository.global_config_get with name' do
477+
expect(facade_repository).to receive(:global_config_get).with('user.name').and_return('Alice')
478+
expect(described_instance.global_config_get('user.name')).to eq('Alice')
479+
end
480+
end
481+
482+
describe '#global_config_list' do
483+
include_context 'with a stubbed facade_repository'
484+
485+
it 'delegates to facade_repository.global_config_list' do
486+
result = { 'user.name' => 'Alice' }
487+
expect(facade_repository).to receive(:global_config_list).and_return(result)
488+
expect(described_instance.global_config_list).to eq(result)
489+
end
490+
end
491+
492+
describe '#global_config_set' do
493+
include_context 'with a stubbed facade_repository'
494+
495+
it 'delegates to facade_repository.global_config_set with name and value' do
496+
set_result = instance_double(Git::CommandLineResult)
497+
expect(facade_repository).to receive(:global_config_set).with('user.name', 'Alice').and_return(set_result)
498+
expect(described_instance.global_config_set('user.name', 'Alice')).to be(set_result)
499+
end
500+
end
463501
end

0 commit comments

Comments
 (0)