git 5.0.0.beta.1 → 5.0.0.beta.2
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/.github/copilot-instructions.md +6 -0
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
- data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
- data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
- data/.github/skills/facade-implementation/SKILL.md +14 -0
- data/.github/skills/facade-test-conventions/SKILL.md +14 -0
- data/.rubocop.yml +5 -0
- data/README.md +51 -11
- data/UPGRADING.md +141 -0
- data/git.gemspec +5 -0
- data/lib/git/branch.rb +7 -18
- data/lib/git/branches.rb +2 -10
- data/lib/git/command_line/base.rb +10 -0
- data/lib/git/command_line/capturing.rb +5 -3
- data/lib/git/command_line/streaming.rb +5 -3
- data/lib/git/command_line.rb +3 -3
- data/lib/git/commands/base.rb +7 -6
- data/lib/git/commands/cat_file/batch.rb +6 -1
- data/lib/git/commands/cat_file/raw.rb +7 -1
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/update_ref/batch.rb +1 -1
- data/lib/git/commands/version.rb +5 -0
- data/lib/git/commands.rb +5 -7
- data/lib/git/config.rb +17 -0
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +665 -0
- data/lib/git/deprecation.rb +9 -0
- data/lib/git/diff.rb +4 -8
- data/lib/git/diff_path_status.rb +2 -13
- data/lib/git/diff_stats.rb +1 -9
- data/lib/git/execution_context/global.rb +3 -28
- data/lib/git/execution_context/repository.rb +30 -41
- data/lib/git/execution_context.rb +43 -24
- data/lib/git/log.rb +3 -9
- data/lib/git/object.rb +14 -21
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/remote.rb +7 -20
- data/lib/git/repository/branching.rb +183 -12
- data/lib/git/repository/committing.rb +64 -68
- data/lib/git/repository/configuring.rb +208 -13
- data/lib/git/repository/context_helpers.rb +264 -0
- data/lib/git/repository/factories.rb +682 -0
- data/lib/git/repository/inspecting.rb +99 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +63 -1
- data/lib/git/repository/object_operations.rb +133 -35
- data/lib/git/repository/path_resolver.rb +1 -1
- data/lib/git/repository/remote_operations.rb +166 -21
- data/lib/git/repository/staging.rb +187 -23
- data/lib/git/repository/stashing.rb +39 -3
- data/lib/git/repository/status_operations.rb +21 -0
- data/lib/git/repository.rb +68 -129
- data/lib/git/stash.rb +2 -9
- data/lib/git/stashes.rb +2 -7
- data/lib/git/status.rb +8 -17
- data/lib/git/version.rb +2 -2
- data/lib/git/worktree.rb +2 -15
- data/lib/git/worktrees.rb +2 -15
- data/lib/git.rb +180 -77
- data/redesign/3_architecture_implementation.md +148 -111
- data/redesign/Phase 4 - Step A.md +360 -0
- data/redesign/beta_release.md +107 -0
- data/redesign/c1c2_audit.md +566 -0
- data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
- data/redesign/config_design.rb +501 -0
- metadata +19 -5
- data/lib/git/base.rb +0 -1204
- data/lib/git/lib.rb +0 -2855
data/lib/git/branch.rb
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'git/base'
|
|
4
3
|
require_relative 'branch_info'
|
|
5
4
|
|
|
6
5
|
module Git
|
|
7
6
|
# Represents a Git branch
|
|
8
7
|
#
|
|
9
8
|
# Branch objects provide access to branch metadata and operations like checkout,
|
|
10
|
-
# delete, and merge. They should be obtained via {Git::
|
|
11
|
-
# {Git::
|
|
9
|
+
# delete, and merge. They should be obtained via {Git::Repository#branch} or
|
|
10
|
+
# {Git::Repository#branches}, not constructed directly.
|
|
12
11
|
#
|
|
13
12
|
# @example Getting a branch
|
|
14
13
|
# git = Git.open('.')
|
|
@@ -24,7 +23,7 @@ module Git
|
|
|
24
23
|
# The full refname of this branch
|
|
25
24
|
#
|
|
26
25
|
# For local branches this is the short name (e.g. `'main'`). For
|
|
27
|
-
# remote-tracking branches obtained via {Git::
|
|
26
|
+
# remote-tracking branches obtained via {Git::Repository#branches} this includes
|
|
28
27
|
# the `remotes/` prefix (e.g. `'remotes/origin/main'`). Branches constructed
|
|
29
28
|
# by {Git::Remote#branch} use the `<remote>/<branch>` form (e.g.
|
|
30
29
|
# `'origin/main'`) which does **not** populate {#remote}.
|
|
@@ -68,17 +67,13 @@ module Git
|
|
|
68
67
|
|
|
69
68
|
# Initialize a new Branch object
|
|
70
69
|
#
|
|
71
|
-
# @param base [Git::
|
|
72
|
-
#
|
|
73
|
-
# Accepts either a {Git::Base} (legacy) or a {Git::Repository} (new form).
|
|
74
|
-
# The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
|
|
75
|
-
# in Phase 4.
|
|
70
|
+
# @param base [Git::Repository] the git repository
|
|
76
71
|
#
|
|
77
72
|
# @param branch_info_or_name [Git::BranchInfo, String] branch info object or name string
|
|
78
73
|
#
|
|
79
74
|
# Passing a BranchInfo is preferred; String support is for backward compatibility.
|
|
80
75
|
#
|
|
81
|
-
# @note Use {Git::
|
|
76
|
+
# @note Use {Git::Repository#branch} or {Git::Repository#branches} instead of constructing directly
|
|
82
77
|
#
|
|
83
78
|
# @api private
|
|
84
79
|
#
|
|
@@ -151,7 +146,7 @@ module Git
|
|
|
151
146
|
#
|
|
152
147
|
# @param file [String] path to the destination archive file
|
|
153
148
|
#
|
|
154
|
-
# @param opts [Hash] archive options (see {Git::
|
|
149
|
+
# @param opts [Hash] archive options (see {Git::Repository#archive})
|
|
155
150
|
#
|
|
156
151
|
# @return [String] the path to the written archive file
|
|
157
152
|
#
|
|
@@ -464,18 +459,12 @@ module Git
|
|
|
464
459
|
nil
|
|
465
460
|
end
|
|
466
461
|
|
|
467
|
-
# Resolves the {Git::Repository} for this branch
|
|
468
|
-
#
|
|
469
|
-
# Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
|
|
470
|
-
# The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
|
|
471
|
-
# in Phase 4.
|
|
472
|
-
#
|
|
473
462
|
# @return [Git::Repository]
|
|
474
463
|
#
|
|
475
464
|
# @api private
|
|
476
465
|
#
|
|
477
466
|
def branch_repository
|
|
478
|
-
@base
|
|
467
|
+
@base
|
|
479
468
|
end
|
|
480
469
|
end
|
|
481
470
|
end
|
data/lib/git/branches.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'git/base'
|
|
4
|
-
|
|
5
3
|
module Git
|
|
6
4
|
# Collection of all Git branches in a repository
|
|
7
5
|
#
|
|
@@ -19,7 +17,7 @@ module Git
|
|
|
19
17
|
|
|
20
18
|
# Creates a new Branches collection populated from the given repository
|
|
21
19
|
#
|
|
22
|
-
# @param base [Git::
|
|
20
|
+
# @param base [Git::Repository] the repository to enumerate
|
|
23
21
|
# branches from
|
|
24
22
|
#
|
|
25
23
|
# @return [void]
|
|
@@ -136,18 +134,12 @@ module Git
|
|
|
136
134
|
|
|
137
135
|
private
|
|
138
136
|
|
|
139
|
-
# Resolves the {Git::Repository} for this collection of branches
|
|
140
|
-
#
|
|
141
|
-
# Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
|
|
142
|
-
# The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
|
|
143
|
-
# in Phase 4.
|
|
144
|
-
#
|
|
145
137
|
# @return [Git::Repository] the repository used to enumerate branches
|
|
146
138
|
#
|
|
147
139
|
# @api private
|
|
148
140
|
#
|
|
149
141
|
def branch_repository
|
|
150
|
-
@base
|
|
142
|
+
@base
|
|
151
143
|
end
|
|
152
144
|
|
|
153
145
|
# Indexes all supported lookup keys for a branch without mutating
|
|
@@ -156,6 +156,11 @@ module Git
|
|
|
156
156
|
#
|
|
157
157
|
# @raise [Git::ProcessIOError] in place of ProcessExecuter::ProcessIOError
|
|
158
158
|
#
|
|
159
|
+
# @raise [Git::ProcessIOError] when a timeout race causes Errno::ESRCH. On Ruby 4.0+,
|
|
160
|
+
# `Process.kill` raises `Errno::ESRCH` when the spawned process exits between the
|
|
161
|
+
# timeout firing and the kill signal being delivered. This is a race in
|
|
162
|
+
# process_executer's timeout handling and is semantically equivalent to a timeout.
|
|
163
|
+
#
|
|
159
164
|
# @return [Object] the return value of the block
|
|
160
165
|
#
|
|
161
166
|
# @api private
|
|
@@ -168,6 +173,11 @@ module Git
|
|
|
168
173
|
raise Git::Error, e.message, cause: e.cause
|
|
169
174
|
rescue ProcessExecuter::ProcessIOError => e
|
|
170
175
|
raise Git::ProcessIOError, e.message, cause: e.cause
|
|
176
|
+
rescue Errno::ESRCH => e
|
|
177
|
+
# Ruby 4.0+: Process.kill raises Errno::ESRCH when the spawned process exits
|
|
178
|
+
# in the narrow window between the timeout firing and the kill signal being sent.
|
|
179
|
+
# This is a known race in process_executer's timeout handling.
|
|
180
|
+
raise Git::ProcessIOError, "Git process no longer exists (timeout race): #{e.message}", cause: e
|
|
171
181
|
end
|
|
172
182
|
|
|
173
183
|
# Build the git command line from the available sources to send to `Process.spawn`
|
|
@@ -9,7 +9,7 @@ module Git
|
|
|
9
9
|
# {Git::CommandLine::Capturing} is the buffering strategy: it calls
|
|
10
10
|
# `ProcessExecuter.run_with_capture`, which reads all subprocess output into
|
|
11
11
|
# `String` objects before returning. Use this class (via
|
|
12
|
-
# {Git::
|
|
12
|
+
# {Git::ExecutionContext#command_capturing}) for the vast majority of git subcommands whose
|
|
13
13
|
# output fits comfortably in memory.
|
|
14
14
|
#
|
|
15
15
|
# {Git::CommandLine::Streaming} is the complementary strategy for commands
|
|
@@ -23,7 +23,7 @@ module Git
|
|
|
23
23
|
# result.stdout # => "abc1234 Initial commit\n..."
|
|
24
24
|
# result.stderr # => ""
|
|
25
25
|
#
|
|
26
|
-
# @see Git::
|
|
26
|
+
# @see Git::ExecutionContext#command_capturing
|
|
27
27
|
#
|
|
28
28
|
# @see Git::CommandLine::Streaming
|
|
29
29
|
#
|
|
@@ -149,7 +149,9 @@ module Git
|
|
|
149
149
|
# @raise [Git::FailedError] if the command returned a non-zero exit status
|
|
150
150
|
#
|
|
151
151
|
# @raise [Git::ProcessIOError] if an exception was raised while collecting
|
|
152
|
-
# subprocess output
|
|
152
|
+
# subprocess output, or (Ruby 4.0+) if a timeout-handling race causes
|
|
153
|
+
# `Errno::ESRCH` when the spawned process exits between the timeout
|
|
154
|
+
# firing and the kill signal being delivered
|
|
153
155
|
#
|
|
154
156
|
# @raise [Git::TimeoutError] if the command times out
|
|
155
157
|
#
|
|
@@ -12,7 +12,7 @@ module Git
|
|
|
12
12
|
# IO object. Stderr is always captured internally in a `StringIO` for error
|
|
13
13
|
# diagnostics and is available as `result.stderr`.
|
|
14
14
|
#
|
|
15
|
-
# Use this class (via {Git::
|
|
15
|
+
# Use this class (via {Git::ExecutionContext#command_streaming}) for commands such as
|
|
16
16
|
# `cat-file -p <blob>` whose stdout may be too large to buffer in memory.
|
|
17
17
|
#
|
|
18
18
|
# {Git::CommandLine::Capturing} is the complementary strategy for the common case
|
|
@@ -26,7 +26,7 @@ module Git
|
|
|
26
26
|
# streaming.run('cat-file', 'blob', sha, out: f)
|
|
27
27
|
# end
|
|
28
28
|
#
|
|
29
|
-
# @see Git::
|
|
29
|
+
# @see Git::ExecutionContext#command_streaming
|
|
30
30
|
#
|
|
31
31
|
# @see Git::CommandLine::Capturing
|
|
32
32
|
#
|
|
@@ -102,7 +102,9 @@ module Git
|
|
|
102
102
|
# @raise [Git::FailedError] if the command returned a non-zero exit status
|
|
103
103
|
#
|
|
104
104
|
# @raise [Git::ProcessIOError] if an exception was raised while collecting
|
|
105
|
-
# subprocess output
|
|
105
|
+
# subprocess output, or (Ruby 4.0+) if a timeout-handling race causes
|
|
106
|
+
# `Errno::ESRCH` when the spawned process exits between the timeout
|
|
107
|
+
# firing and the kill signal being delivered
|
|
106
108
|
#
|
|
107
109
|
# @raise [Git::TimeoutError] if the command times out
|
|
108
110
|
#
|
data/lib/git/command_line.rb
CHANGED
|
@@ -14,9 +14,9 @@ module Git
|
|
|
14
14
|
# Use this for commands (e.g. `cat-file -p <blob>`) whose output may be
|
|
15
15
|
# too large to buffer.
|
|
16
16
|
#
|
|
17
|
-
# Both classes inherit from {Git::CommandLine::Base} and are
|
|
18
|
-
#
|
|
19
|
-
# {Git::
|
|
17
|
+
# Both classes inherit from {Git::CommandLine::Base} and are used internally
|
|
18
|
+
# by {Git::ExecutionContext#command_capturing} and
|
|
19
|
+
# {Git::ExecutionContext#command_streaming}.
|
|
20
20
|
#
|
|
21
21
|
# Results are returned as {Git::CommandLine::Result} objects (also accessible
|
|
22
22
|
# as {Git::CommandLineResult} for backward compatibility).
|
data/lib/git/commands/base.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'git/commands/arguments'
|
|
4
|
+
require 'git/version_constraint'
|
|
4
5
|
|
|
5
6
|
module Git
|
|
6
7
|
module Commands
|
|
@@ -173,8 +174,8 @@ module Git
|
|
|
173
174
|
end
|
|
174
175
|
end
|
|
175
176
|
|
|
176
|
-
# @param execution_context [Git::ExecutionContext
|
|
177
|
-
# {Git::
|
|
177
|
+
# @param execution_context [Git::ExecutionContext] context that provides
|
|
178
|
+
# {Git::ExecutionContext#command_capturing} and {Git::ExecutionContext#command_streaming}
|
|
178
179
|
def initialize(execution_context)
|
|
179
180
|
@execution_context = execution_context
|
|
180
181
|
end
|
|
@@ -210,7 +211,7 @@ module Git
|
|
|
210
211
|
# @raise [Git::VersionError] if the installed git version doesn't meet requirements
|
|
211
212
|
def call(*, **)
|
|
212
213
|
bound = args_definition.bind(*, **)
|
|
213
|
-
validate_version!
|
|
214
|
+
validate_version!(bound.execution_options)
|
|
214
215
|
result = execute_command(bound)
|
|
215
216
|
validate_exit_status!(result)
|
|
216
217
|
result
|
|
@@ -312,10 +313,10 @@ module Git
|
|
|
312
313
|
#
|
|
313
314
|
# Floor check always runs first and fails fast.
|
|
314
315
|
#
|
|
315
|
-
def validate_version!
|
|
316
|
+
def validate_version!(exec_opts = {})
|
|
316
317
|
return if self.class.skip_version_validation?
|
|
317
318
|
|
|
318
|
-
actual_version = @execution_context.git_version
|
|
319
|
+
actual_version = @execution_context.git_version(timeout: exec_opts[:timeout])
|
|
319
320
|
|
|
320
321
|
# Floor check: fail-fast if git is too old for the gem itself
|
|
321
322
|
validate_floor_version!(actual_version)
|
|
@@ -351,7 +352,7 @@ module Git
|
|
|
351
352
|
# the read end. The write and close happen concurrently with the block.
|
|
352
353
|
#
|
|
353
354
|
# The read end can be passed as the `in:` keyword to
|
|
354
|
-
# {Git::
|
|
355
|
+
# {Git::ExecutionContext#command_capturing} / {Git::CommandLine#run_with_capture}, connecting it directly to
|
|
355
356
|
# the spawned git process's stdin without an intermediate file or shell
|
|
356
357
|
# heredoc. This is required because `Process.spawn` only accepts real IO
|
|
357
358
|
# objects with a file descriptor — `StringIO` does not work.
|
|
@@ -103,6 +103,9 @@ module Git
|
|
|
103
103
|
# When provided, {#call} dispatches to the streaming execution path.
|
|
104
104
|
execution_option :out
|
|
105
105
|
|
|
106
|
+
# Abort the command after this many seconds.
|
|
107
|
+
execution_option :timeout
|
|
108
|
+
|
|
106
109
|
# Object names (or batch-command lines) are written to stdin, not argv.
|
|
107
110
|
# Using skip_cli: true because these values are fed via stdin — git never
|
|
108
111
|
# sees them as CLI arguments so Ruby must enforce the cross-argument
|
|
@@ -302,6 +305,8 @@ module Git
|
|
|
302
305
|
# @option options [#write, nil] :out (nil) stream stdout to this IO object
|
|
303
306
|
# instead of buffering in memory; when given, `result.stdout` will be `''`
|
|
304
307
|
#
|
|
308
|
+
# @option options [Numeric, nil] :timeout (nil) abort the command after this many seconds
|
|
309
|
+
#
|
|
305
310
|
# @return [Git::CommandLineResult] the result of calling `git cat-file`
|
|
306
311
|
#
|
|
307
312
|
# Stdout contains one metadata line per object (or `''` when `out:` is given)
|
|
@@ -311,7 +316,7 @@ module Git
|
|
|
311
316
|
# @raise [Git::FailedError] if git exits non-zero
|
|
312
317
|
def call(*objects, **)
|
|
313
318
|
bound = args_definition.bind(*objects, **)
|
|
314
|
-
validate_version!
|
|
319
|
+
validate_version!(bound.execution_options)
|
|
315
320
|
# `-Z` puts git into NUL I/O mode: input objects must be NUL-terminated.
|
|
316
321
|
# Without `-Z`, the standard newline delimiter is used.
|
|
317
322
|
delimiter = bound.Z? ? "\0" : "\n"
|
|
@@ -62,6 +62,9 @@ module Git
|
|
|
62
62
|
# When provided, {#call} dispatches to the streaming execution path.
|
|
63
63
|
execution_option :out
|
|
64
64
|
|
|
65
|
+
# Abort the command after this many seconds.
|
|
66
|
+
execution_option :timeout
|
|
67
|
+
|
|
65
68
|
end_of_options
|
|
66
69
|
|
|
67
70
|
# Expected object type — one of `commit`, `tree`, `blob`, or `tag`.
|
|
@@ -191,9 +194,12 @@ module Git
|
|
|
191
194
|
#
|
|
192
195
|
# @raise [Git::FailedError] if the object does not exist or is not of the
|
|
193
196
|
# given type
|
|
197
|
+
#
|
|
198
|
+
# @option options [Numeric, nil] :timeout (nil) abort the command after this many seconds
|
|
199
|
+
#
|
|
194
200
|
def call(*, **)
|
|
195
201
|
bound = args_definition.bind(*, **)
|
|
196
|
-
validate_version!
|
|
202
|
+
validate_version!(bound.execution_options)
|
|
197
203
|
result = execute_command(bound)
|
|
198
204
|
|
|
199
205
|
# `-e` treats exit 1 as a meaningful result (object not found), but any other
|
|
@@ -45,6 +45,9 @@ module Git
|
|
|
45
45
|
# General read options
|
|
46
46
|
flag_option :includes, negatable: true
|
|
47
47
|
|
|
48
|
+
# Output modifiers
|
|
49
|
+
flag_option :show_scope
|
|
50
|
+
|
|
48
51
|
# Operands
|
|
49
52
|
end_of_options
|
|
50
53
|
operand :name, required: true
|
|
@@ -91,6 +94,8 @@ module Git
|
|
|
91
94
|
# @option options [Boolean, nil] :no_includes (nil) disable include directives
|
|
92
95
|
# in config files (`--no-includes`)
|
|
93
96
|
#
|
|
97
|
+
# @option options [Boolean, nil] :show_scope (nil) show the scope of each config entry
|
|
98
|
+
#
|
|
94
99
|
# @return [Git::CommandLineResult] the result of calling `git config --get-urlmatch`
|
|
95
100
|
#
|
|
96
101
|
# @raise [ArgumentError] if unsupported options are provided
|
|
@@ -86,7 +86,7 @@ module Git
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
bound = args_definition.bind(*, exclude_existing: exclude_existing, **)
|
|
89
|
-
validate_version!
|
|
89
|
+
validate_version!(bound.execution_options)
|
|
90
90
|
stdin = Array(bound.ref).map { |r| "#{r}\n" }.join
|
|
91
91
|
with_stdin(stdin) { |reader| run_filter(bound, reader) }
|
|
92
92
|
end
|
|
@@ -118,7 +118,7 @@ module Git
|
|
|
118
118
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
119
119
|
def call(*, **)
|
|
120
120
|
bound = args_definition.bind(*, **)
|
|
121
|
-
validate_version!
|
|
121
|
+
validate_version!(bound.execution_options)
|
|
122
122
|
with_stdin(build_stdin(bound)) do |reader|
|
|
123
123
|
result = @execution_context.command_capturing(
|
|
124
124
|
*bound, in: reader, **bound.execution_options, raise_on_failure: false
|
data/lib/git/commands/version.rb
CHANGED
|
@@ -28,6 +28,7 @@ module Git
|
|
|
28
28
|
skip_version_validation
|
|
29
29
|
|
|
30
30
|
arguments do
|
|
31
|
+
execution_option :timeout
|
|
31
32
|
literal 'version'
|
|
32
33
|
flag_option :build_options
|
|
33
34
|
end
|
|
@@ -42,6 +43,10 @@ module Git
|
|
|
42
43
|
#
|
|
43
44
|
# @option options [Boolean, nil] :build_options (nil) include build options in the output
|
|
44
45
|
#
|
|
46
|
+
# @option options [Numeric, nil] :timeout (nil) the number of seconds to wait
|
|
47
|
+
# for the command to complete; if nil, uses the global timeout from
|
|
48
|
+
# {Git::Config}; if 0, no timeout is enforced
|
|
49
|
+
#
|
|
45
50
|
# @return [Git::CommandLineResult] the result of calling `git version`
|
|
46
51
|
#
|
|
47
52
|
# @raise [ArgumentError] if unsupported options are provided
|
data/lib/git/commands.rb
CHANGED
|
@@ -61,19 +61,17 @@ module Git
|
|
|
61
61
|
# {Git::CommandLine}, and return a raw {Git::CommandLineResult}.
|
|
62
62
|
#
|
|
63
63
|
# Commands do **not** parse output — that responsibility belongs to the
|
|
64
|
-
# {Git::Parsers} layer, orchestrated by the facade ({Git::
|
|
65
|
-
# {Git::Repository}).
|
|
64
|
+
# {Git::Parsers} layer, orchestrated by the facade ({Git::Repository}).
|
|
66
65
|
#
|
|
67
66
|
# All classes in this namespace are internal (`@api private`). End users
|
|
68
|
-
# should interact with the public API on {Git::
|
|
67
|
+
# should interact with the public API on {Git::Repository} instead.
|
|
69
68
|
#
|
|
70
69
|
# ## Architecture
|
|
71
70
|
#
|
|
72
71
|
# ```
|
|
73
|
-
# Git::
|
|
74
|
-
# └── Git::
|
|
75
|
-
# └── Git::
|
|
76
|
-
# └── Git::CommandLine (subprocess execution)
|
|
72
|
+
# Git::Repository (public API / facade — orchestrates commands + parsers)
|
|
73
|
+
# └── Git::Commands::* (defines CLI API, binds args, executes)
|
|
74
|
+
# └── Git::CommandLine (subprocess execution)
|
|
77
75
|
# ```
|
|
78
76
|
#
|
|
79
77
|
# Simple commands inherit from {Commands::Base} and only need an `arguments`
|
data/lib/git/config.rb
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
module Git
|
|
4
4
|
# The global configuration for this gem
|
|
5
5
|
class Config
|
|
6
|
+
# Returns the process-wide singleton {Git::Config} instance
|
|
7
|
+
#
|
|
8
|
+
# All calls to {Git.configure}, {Git.config}, and the {Git::ExecutionContext}
|
|
9
|
+
# classes resolve global configuration through this method.
|
|
10
|
+
#
|
|
11
|
+
# @example Read the configured binary path
|
|
12
|
+
# Git::Config.instance.binary_path #=> "git"
|
|
13
|
+
#
|
|
14
|
+
# @example Mutate the singleton (same as Git.configure { |c| ... })
|
|
15
|
+
# Git::Config.instance.binary_path = '/usr/local/bin/git'
|
|
16
|
+
#
|
|
17
|
+
# @return [Git::Config] the singleton config object
|
|
18
|
+
#
|
|
19
|
+
def self.instance
|
|
20
|
+
@instance ||= new
|
|
21
|
+
end
|
|
22
|
+
|
|
6
23
|
attr_writer :binary_path, :git_ssh, :timeout
|
|
7
24
|
|
|
8
25
|
def initialize
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
# Represents a single Git configuration entry
|
|
5
|
+
#
|
|
6
|
+
# Returned by {Git::Configuring} read operations such as {Git::Configuring#config_get},
|
|
7
|
+
# {Git::Configuring#config_get_all}, {Git::Configuring#config_list}, and their
|
|
8
|
+
# related methods.
|
|
9
|
+
#
|
|
10
|
+
# @example Create a ConfigEntryInfo
|
|
11
|
+
# entry = Git::ConfigEntryInfo.new(
|
|
12
|
+
# scope: 'local',
|
|
13
|
+
# origin: 'file:.git/config',
|
|
14
|
+
# key: 'remote.origin.url',
|
|
15
|
+
# value: 'https://github.com/ruby-git/ruby-git'
|
|
16
|
+
# )
|
|
17
|
+
# entry.section # => "remote"
|
|
18
|
+
# entry.subsection # => "origin"
|
|
19
|
+
# entry.variable # => "url"
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
#
|
|
23
|
+
# @!attribute [r] scope
|
|
24
|
+
#
|
|
25
|
+
# The scope of the configuration entry
|
|
26
|
+
#
|
|
27
|
+
# May be one of `"system"`, `"global"`, `"local"`, `"worktree"`, `"command"`,
|
|
28
|
+
# `"file"`, or `"blob"`. The `"command"` scope is used for values supplied
|
|
29
|
+
# via the command line (including default values from `--default`).
|
|
30
|
+
#
|
|
31
|
+
# @return [String] the config scope string (e.g. `"local"`, `"global"`)
|
|
32
|
+
#
|
|
33
|
+
# @!attribute [r] origin
|
|
34
|
+
#
|
|
35
|
+
# Where the configuration entry originates
|
|
36
|
+
#
|
|
37
|
+
# The origin is in the format `<origin-type>:<actual-origin>` and is never
|
|
38
|
+
# blank. The origin type prefix is one of `file:`, `blob:`, `command line:`,
|
|
39
|
+
# or `standard input:`.
|
|
40
|
+
#
|
|
41
|
+
# `nil` when the git command used to retrieve this entry does not support
|
|
42
|
+
# `--show-origin` (currently only `--get-urlmatch`).
|
|
43
|
+
#
|
|
44
|
+
# @return [String, nil] the origin path in the format `<type>:<path>`, or `nil`
|
|
45
|
+
#
|
|
46
|
+
# @!attribute [r] key
|
|
47
|
+
#
|
|
48
|
+
# The full dotted key name of the configuration entry (e.g., `remote.origin.url`)
|
|
49
|
+
#
|
|
50
|
+
# @return [String] the full dotted key name (e.g. `remote.origin.url`)
|
|
51
|
+
#
|
|
52
|
+
# @!attribute [r] value
|
|
53
|
+
#
|
|
54
|
+
# The value of the configuration entry
|
|
55
|
+
#
|
|
56
|
+
# @return [String] the string value of this entry
|
|
57
|
+
#
|
|
58
|
+
ConfigEntryInfo = Data.define(:scope, :origin, :key, :value) do
|
|
59
|
+
# Returns the section component of the key (everything before the first dot)
|
|
60
|
+
#
|
|
61
|
+
# Returns an empty string when the key contains no dot.
|
|
62
|
+
#
|
|
63
|
+
# @example Section component of a dotted key
|
|
64
|
+
# entry.section # => "remote"
|
|
65
|
+
#
|
|
66
|
+
# @return [String] the section name, or an empty string when the key has no dot
|
|
67
|
+
#
|
|
68
|
+
def section = first_dot ? key[0...first_dot] : ''
|
|
69
|
+
|
|
70
|
+
# Returns the subsection component of the key (everything between the first and last dot)
|
|
71
|
+
#
|
|
72
|
+
# Returns an empty string when the key has zero or one dot (no subsection).
|
|
73
|
+
#
|
|
74
|
+
# @example Subsection component of a dotted key
|
|
75
|
+
# entry.subsection # => "origin"
|
|
76
|
+
#
|
|
77
|
+
# @return [String] the subsection name, or an empty string when there is no subsection
|
|
78
|
+
#
|
|
79
|
+
def subsection = first_dot && first_dot != last_dot ? key[(first_dot + 1)...last_dot] : ''
|
|
80
|
+
|
|
81
|
+
# Returns the variable component of the key (everything after the last dot)
|
|
82
|
+
#
|
|
83
|
+
# Returns the full key when the key contains no dot.
|
|
84
|
+
#
|
|
85
|
+
# @example Variable component of a dotted key
|
|
86
|
+
# entry.variable # => "url"
|
|
87
|
+
#
|
|
88
|
+
# @return [String] the variable name (everything after the last dot)
|
|
89
|
+
#
|
|
90
|
+
def variable = last_dot ? key[(last_dot + 1)..] : key
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# Returns the index of the first dot in the key, or nil if none exists
|
|
95
|
+
#
|
|
96
|
+
# @return [Integer, nil] the zero-based index of the first dot, or `nil`
|
|
97
|
+
#
|
|
98
|
+
def first_dot = key.index('.')
|
|
99
|
+
|
|
100
|
+
# Returns the index of the last dot in the key, or nil if none exists
|
|
101
|
+
#
|
|
102
|
+
# @return [Integer, nil] the zero-based index of the last dot, or `nil`
|
|
103
|
+
#
|
|
104
|
+
def last_dot = key.rindex('.')
|
|
105
|
+
end
|
|
106
|
+
end
|