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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +6 -0
  3. data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
  4. data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
  5. data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
  6. data/.github/skills/facade-implementation/SKILL.md +14 -0
  7. data/.github/skills/facade-test-conventions/SKILL.md +14 -0
  8. data/.rubocop.yml +5 -0
  9. data/README.md +51 -11
  10. data/UPGRADING.md +141 -0
  11. data/git.gemspec +5 -0
  12. data/lib/git/branch.rb +7 -18
  13. data/lib/git/branches.rb +2 -10
  14. data/lib/git/command_line/base.rb +10 -0
  15. data/lib/git/command_line/capturing.rb +5 -3
  16. data/lib/git/command_line/streaming.rb +5 -3
  17. data/lib/git/command_line.rb +3 -3
  18. data/lib/git/commands/base.rb +7 -6
  19. data/lib/git/commands/cat_file/batch.rb +6 -1
  20. data/lib/git/commands/cat_file/raw.rb +7 -1
  21. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
  22. data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
  23. data/lib/git/commands/update_ref/batch.rb +1 -1
  24. data/lib/git/commands/version.rb +5 -0
  25. data/lib/git/commands.rb +5 -7
  26. data/lib/git/config.rb +17 -0
  27. data/lib/git/config_entry_info.rb +106 -0
  28. data/lib/git/configuring.rb +665 -0
  29. data/lib/git/deprecation.rb +9 -0
  30. data/lib/git/diff.rb +4 -8
  31. data/lib/git/diff_path_status.rb +2 -13
  32. data/lib/git/diff_stats.rb +1 -9
  33. data/lib/git/execution_context/global.rb +3 -28
  34. data/lib/git/execution_context/repository.rb +30 -41
  35. data/lib/git/execution_context.rb +43 -24
  36. data/lib/git/log.rb +3 -9
  37. data/lib/git/object.rb +14 -21
  38. data/lib/git/parsers/config_entry.rb +110 -0
  39. data/lib/git/parsers/ls_remote.rb +79 -0
  40. data/lib/git/remote.rb +7 -20
  41. data/lib/git/repository/branching.rb +183 -12
  42. data/lib/git/repository/committing.rb +64 -68
  43. data/lib/git/repository/configuring.rb +208 -13
  44. data/lib/git/repository/context_helpers.rb +264 -0
  45. data/lib/git/repository/factories.rb +682 -0
  46. data/lib/git/repository/inspecting.rb +99 -0
  47. data/lib/git/repository/maintenance.rb +65 -0
  48. data/lib/git/repository/merging.rb +63 -1
  49. data/lib/git/repository/object_operations.rb +133 -35
  50. data/lib/git/repository/path_resolver.rb +1 -1
  51. data/lib/git/repository/remote_operations.rb +166 -21
  52. data/lib/git/repository/staging.rb +187 -23
  53. data/lib/git/repository/stashing.rb +39 -3
  54. data/lib/git/repository/status_operations.rb +21 -0
  55. data/lib/git/repository.rb +68 -129
  56. data/lib/git/stash.rb +2 -9
  57. data/lib/git/stashes.rb +2 -7
  58. data/lib/git/status.rb +8 -17
  59. data/lib/git/version.rb +2 -2
  60. data/lib/git/worktree.rb +2 -15
  61. data/lib/git/worktrees.rb +2 -15
  62. data/lib/git.rb +180 -77
  63. data/redesign/3_architecture_implementation.md +148 -111
  64. data/redesign/Phase 4 - Step A.md +360 -0
  65. data/redesign/beta_release.md +107 -0
  66. data/redesign/c1c2_audit.md +566 -0
  67. data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
  68. data/redesign/config_design.rb +501 -0
  69. metadata +19 -5
  70. data/lib/git/base.rb +0 -1204
  71. data/lib/git/lib.rb +0 -2855
data/lib/git/diff.rb CHANGED
@@ -25,7 +25,7 @@ module Git
25
25
  # @example
26
26
  # diff = Git::Diff.new(base, 'HEAD~1', 'HEAD')
27
27
  #
28
- # @param base [Git::Base, Git::Repository] the git repository
28
+ # @param base [Git::Repository] the git repository
29
29
  #
30
30
  # @param from [String, nil] the starting commit ref, or `nil` to compare
31
31
  # from the index
@@ -102,11 +102,7 @@ module Git
102
102
  # @return [String] the raw output of `git diff`
103
103
  #
104
104
  def patch
105
- if @base.respond_to?(:diff_full)
106
- @base.diff_full(@from, @to, path_limiter: @path)
107
- else
108
- @base.lib.diff_full(@from, @to, { path_limiter: @path })
109
- end
105
+ @base.diff_full(@from, @to, path_limiter: @path)
110
106
  end
111
107
  alias to_s patch
112
108
 
@@ -287,7 +283,7 @@ module Git
287
283
  # mode: '100644', src: 'abc123', dst: 'def456',
288
284
  # type: 'modified', binary: false)
289
285
  #
290
- # @param base [Git::Base, Git::Repository] the git repository
286
+ # @param base [Git::Repository] the git repository
291
287
  #
292
288
  # @param hash [Hash] the parsed diff attributes
293
289
  #
@@ -402,7 +398,7 @@ module Git
402
398
  class FullDiffParser
403
399
  # Creates a new FullDiffParser
404
400
  #
405
- # @param base [Git::Base, Git::Repository] the git repository
401
+ # @param base [Git::Repository] the git repository
406
402
  #
407
403
  # @param patch_text [String] the raw `git diff` output to parse
408
404
  #
@@ -61,26 +61,15 @@ module Git
61
61
 
62
62
  # Lazily fetches and caches the path status from the git lib
63
63
  #
64
- # When constructed with a pre-fetched hash, returns it directly.
65
- # When `@base` responds to `#diff_name_status` (e.g. {Git::Repository} or
66
- # {Git::Base}, which defines it as an alias for `#diff_path_status`),
67
- # delegates directly to that method. Otherwise falls back to the legacy
68
- # `@base.lib.diff_path_status` call for duck-typed base objects that only
69
- # expose path-status via their lib.
70
- #
71
64
  # @return [Hash{String => String}] a mapping of file paths to status codes
72
65
  #
73
66
  def fetch_path_status
74
- @fetch_path_status ||= if @base.respond_to?(:diff_name_status)
75
- @base.diff_name_status(@from, @to, path_limiter: @path_limiter).to_h
76
- else
77
- @base.lib.diff_path_status(@from, @to, { path_limiter: @path_limiter })
78
- end
67
+ @fetch_path_status ||= @base.diff_name_status(@from, @to, path_limiter: @path_limiter).to_h
79
68
  end
80
69
 
81
70
  # Sets up legacy (base, from, to, path_limiter) instance state
82
71
  #
83
- # @param base [Git::Base, Git::Repository] the git base instance
72
+ # @param base [Git::Repository] the git object
84
73
  #
85
74
  # @param from [String] the first commit or object to compare
86
75
  #
@@ -88,17 +88,9 @@ module Git
88
88
 
89
89
  # Lazily fetches and caches the stats from the git lib
90
90
  #
91
- # When `@base` implements `#diff_numstat`, delegates directly to that
92
- # method. Otherwise falls back to the legacy `@base.lib.diff_stats` call
93
- # so that existing `Git::Base`-backed callers continue to work unchanged.
94
- #
95
91
  # @return [Hash] the fetched stats hash
96
92
  def fetch_stats
97
- @fetch_stats ||= if @base.respond_to?(:diff_numstat)
98
- @base.diff_numstat(@from, @to, path_limiter: @path_limiter)
99
- else
100
- @base.lib.diff_stats(@from, @to, { path_limiter: @path_limiter })
101
- end
93
+ @fetch_stats ||= @base.diff_numstat(@from, @to, path_limiter: @path_limiter)
102
94
  end
103
95
  end
104
96
  end
@@ -20,37 +20,12 @@ module Git
20
20
  # @example Create a context targeting a specific binary
21
21
  # context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
22
22
  #
23
+ # @see Git::ExecutionContext#initialize for constructor parameters and
24
+ # their semantics
25
+ #
23
26
  # @api private
24
27
  #
25
28
  class Global < ExecutionContext
26
- # Creates a new global execution context
27
- #
28
- # @example Create with default settings
29
- # Git::ExecutionContext::Global.new
30
- #
31
- # @example Create with an explicit binary path
32
- # Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
33
- #
34
- # @param binary_path [String, :use_global_config] path to the git binary
35
- #
36
- # Give `:use_global_config` (the default) to use `Git::Base.config.binary_path`.
37
- #
38
- # Passing `nil` raises `ArgumentError` — there is no "unset the
39
- # binary" semantic.
40
- #
41
- # @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
42
- #
43
- # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default) to use `Git::Base.config.git_ssh`.
44
- #
45
- # @param logger [Logger, nil] the logger to use in the CommandLine layer
46
- #
47
- # Give `nil` to use a null logger (`Logger.new(nil)`).
48
- #
49
- # @raise [ArgumentError] if `binary_path` is `nil`
50
- #
51
- def initialize(binary_path: :use_global_config, git_ssh: :use_global_config, logger: nil)
52
- super
53
- end
54
29
  end
55
30
  end
56
31
  end
@@ -13,15 +13,10 @@ module Git
13
13
  #
14
14
  # ### Construction
15
15
  #
16
- # Prefer the factory class methods over `new` when building from a
17
- # {Git::Base} object or a hash:
16
+ # Use {.from_hash} to build from a configuration hash:
18
17
  #
19
- # context = Git::ExecutionContext::Repository.from_base(base)
20
18
  # context = Git::ExecutionContext::Repository.from_hash(repository: '/repo/.git', ...)
21
19
  #
22
- # @example Build from a Git::Base object
23
- # context = Git::ExecutionContext::Repository.from_base(git)
24
- #
25
20
  # @example Build from a configuration hash
26
21
  # context = Git::ExecutionContext::Repository.from_hash(
27
22
  # repository: '/path/to/.git',
@@ -31,31 +26,6 @@ module Git
31
26
  # @api private
32
27
  #
33
28
  class Repository < ExecutionContext
34
- # Creates a Repository context from a {Git::Base} instance
35
- #
36
- # @example Build from a base object
37
- # git = Git.open('/path/to/repo')
38
- # context = Git::ExecutionContext::Repository.from_base(git)
39
- #
40
- # @param base_object [Git::Base] the base Git object to derive context from
41
- #
42
- # @param logger [Logger, nil] logger forwarded to the CommandLine layer;
43
- # `nil` uses a null logger (see {Git::ExecutionContext#initialize})
44
- #
45
- # @return [Git::ExecutionContext::Repository] the new repository context
46
- #
47
- def self.from_base(base_object, logger: nil)
48
- new(
49
- git_dir: base_object.repo.to_s,
50
- git_index_file: base_object.index&.to_s,
51
- git_work_dir: base_object.dir&.to_s,
52
- base_object: base_object,
53
- git_ssh: base_object.git_ssh,
54
- binary_path: base_object.binary_path,
55
- logger: logger
56
- )
57
- end
58
-
59
29
  # Creates a Repository context from a Hash
60
30
  #
61
31
  # Expected keys: `:repository`, `:working_directory`, `:index`, `:git_ssh`,
@@ -79,8 +49,8 @@ module Git
79
49
  git_dir: base_hash[:repository],
80
50
  git_index_file: base_hash[:index],
81
51
  git_work_dir: base_hash[:working_directory],
82
- git_ssh: base_hash.key?(:git_ssh) ? base_hash[:git_ssh] : :use_global_config,
83
- binary_path: base_hash.key?(:binary_path) ? base_hash[:binary_path] : :use_global_config,
52
+ git_ssh: base_hash.fetch(:git_ssh, :use_global_config),
53
+ binary_path: base_hash.fetch(:binary_path, :use_global_config),
84
54
  logger: logger
85
55
  )
86
56
  end
@@ -96,18 +66,18 @@ module Git
96
66
  #
97
67
  # @param git_index_file [String, nil] path to the index file
98
68
  #
99
- # @param base_object [Git::Base, nil] originating base object
100
- #
101
69
  # @param binary_path [String, :use_global_config] path to the git binary
102
70
  #
103
- # Give `:use_global_config` (the default) to use `Git::Base.config.binary_path`.
71
+ # Give `:use_global_config` (the default) to use
72
+ # `Git::Config.instance.binary_path`.
104
73
  #
105
74
  # Passing `nil` raises `ArgumentError` — there is no "unset the
106
75
  # binary" semantic.
107
76
  #
108
77
  # @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
109
78
  #
110
- # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default) to use `Git::Base.config.git_ssh`.
79
+ # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default)
80
+ # to use `Git::Config.instance.git_ssh`.
111
81
  #
112
82
  # @param logger [Logger, nil] the logger to use in the CommandLine layer
113
83
  #
@@ -119,7 +89,6 @@ module Git
119
89
  git_dir:,
120
90
  git_work_dir: nil,
121
91
  git_index_file: nil,
122
- base_object: nil,
123
92
  binary_path: :use_global_config,
124
93
  git_ssh: :use_global_config,
125
94
  logger: nil
@@ -128,7 +97,6 @@ module Git
128
97
  @git_dir = git_dir
129
98
  @git_work_dir = git_work_dir
130
99
  @git_index_file = git_index_file
131
- @base_object = base_object
132
100
  end
133
101
 
134
102
  # @return [String, nil] path to the `.git` directory
@@ -140,8 +108,29 @@ module Git
140
108
  # @return [String, nil] path to the index file
141
109
  attr_reader :git_index_file
142
110
 
143
- # @return [Git::Base, nil] originating base object
144
- attr_reader :base_object
111
+ # Returns a new instance with the same configuration, applying `overrides`
112
+ #
113
+ # Uses raw stored values for `binary_path`, `git_ssh`, and `logger` so
114
+ # that `:use_global_config` sentinels are preserved across rebuilds and
115
+ # future changes to `Git.configure` continue to take effect.
116
+ #
117
+ # @param overrides [Hash] keyword arguments to override in the new instance
118
+ #
119
+ # @return [Git::ExecutionContext::Repository] the new context
120
+ #
121
+ # @api private
122
+ #
123
+ def dup_with(**overrides)
124
+ self.class.new(
125
+ git_dir: @git_dir,
126
+ git_work_dir: @git_work_dir,
127
+ git_index_file: @git_index_file,
128
+ binary_path: @binary_path,
129
+ git_ssh: @git_ssh,
130
+ logger: @logger,
131
+ **overrides
132
+ )
133
+ end
145
134
  end
146
135
  end
147
136
  end
@@ -88,20 +88,22 @@ module Git
88
88
  #
89
89
  # @param binary_path [String, :use_global_config] path to the git binary
90
90
  #
91
- # Give `:use_global_config` (the default) to use `Git::Base.config.binary_path`.
91
+ # Give `:use_global_config` (the default) to use `Git::Config.instance.binary_path`.
92
92
  #
93
93
  # Passing `nil` raises `ArgumentError` — there is no "unset the
94
94
  # binary" semantic.
95
95
  #
96
96
  # @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
97
97
  #
98
- # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default) to use `Git::Base.config.git_ssh`.
98
+ # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default)
99
+ # to use `Git::Config.instance.git_ssh`.
99
100
  #
100
101
  # @param logger [Logger, nil] the logger to use in the CommandLine layer
101
102
  #
102
103
  # Give `nil` to use a null logger (`Logger.new(nil)`).
103
104
  #
104
- # @raise [NotImplementedError] if called directly on {Git::ExecutionContext} rather than a subclass
105
+ # @raise [NotImplementedError] if called directly on {Git::ExecutionContext}
106
+ # rather than a subclass
105
107
  #
106
108
  # @raise [ArgumentError] if `binary_path` is `nil`
107
109
  #
@@ -156,11 +158,12 @@ module Git
156
158
 
157
159
  # Returns the resolved git binary path for this context
158
160
  #
159
- # `:use_global_config` is resolved to `Git::Base.config.binary_path` each time a
160
- # command method is called, so runtime changes to `Git::Base.config.binary_path`
161
+ # `:use_global_config` is resolved to `Git::Config.instance.binary_path` each time a
162
+ # command method is called, so runtime changes to
163
+ # `Git.configure { |c| c.binary_path = ... }`
161
164
  # are reflected per command invocation.
162
165
  #
163
- # @example With the default sentinel (resolves from Git::Base.config at call-time)
166
+ # @example With the default sentinel (resolves from Git::Config.instance at call-time)
164
167
  # context = Git::ExecutionContext::Global.new
165
168
  # context.binary_path #=> "git"
166
169
  #
@@ -171,19 +174,20 @@ module Git
171
174
  # @return [String] the resolved git binary path
172
175
  #
173
176
  def binary_path
174
- return Git::Base.config.binary_path if @binary_path == :use_global_config
177
+ return Git::Config.instance.binary_path if @binary_path == :use_global_config
175
178
 
176
179
  @binary_path
177
180
  end
178
181
 
179
182
  # Returns the resolved `GIT_SSH` wrapper path for this context
180
183
  #
181
- # `:use_global_config` is resolved to `Git::Base.config.git_ssh` each time a
182
- # command method is called, so runtime changes to `Git::Base.config.git_ssh`
184
+ # `:use_global_config` is resolved to `Git::Config.instance.git_ssh` each time a
185
+ # command method is called, so runtime changes to
186
+ # `Git.configure { |c| c.git_ssh = ... }`
183
187
  # are reflected per command invocation. `nil` means the variable will be
184
188
  # explicitly unset.
185
189
  #
186
- # @example With the default sentinel (resolves from Git::Base.config at call-time)
190
+ # @example With the default sentinel (resolves from Git::Config.instance at call-time)
187
191
  # context = Git::ExecutionContext::Global.new
188
192
  # context.git_ssh #=> nil
189
193
  #
@@ -194,11 +198,23 @@ module Git
194
198
  # @return [String, nil] the resolved `GIT_SSH` wrapper path, or `nil` to unset
195
199
  #
196
200
  def git_ssh
197
- return Git::Base.config.git_ssh if @git_ssh == :use_global_config
201
+ return Git::Config.instance.git_ssh if @git_ssh == :use_global_config
198
202
 
199
203
  @git_ssh
200
204
  end
201
205
 
206
+ # Returns the logger used by this context
207
+ #
208
+ # @example
209
+ # context = Git::ExecutionContext::Repository.new(git_dir: '/repo/.git', logger: my_logger)
210
+ # context.logger #=> my_logger
211
+ #
212
+ # @return [Logger] the logger instance; never `nil`
213
+ #
214
+ # @api private
215
+ #
216
+ attr_reader :logger
217
+
202
218
  # Runs a git command and returns the result
203
219
  #
204
220
  # By default, raises {Git::FailedError} if the command exits with a non-zero
@@ -393,25 +409,24 @@ module Git
393
409
 
394
410
  # Returns the installed git version
395
411
  #
396
- # The result is memoized per instance.
412
+ # The result is memoized per instance. Accepts an optional timeout used
413
+ # only when the version has not yet been fetched for this context.
397
414
  #
398
- # @example Get the installed git version
399
- # context = Git::ExecutionContext::Global.new
400
- # context.git_version #=> #<Git::Version 2.42.0>
415
+ # @param timeout [Numeric, nil] seconds to wait for `git version`; `nil`
416
+ # falls back to the global {Git::Config} timeout; `0` disables the timeout
417
+ # entirely (the command runs until it completes or the process is killed)
401
418
  #
402
419
  # @return [Git::Version] the installed git version
403
420
  #
404
421
  # @raise [Git::UnexpectedResultError] if the version string cannot be parsed
405
422
  #
406
- def git_version
423
+ def git_version(timeout: nil)
407
424
  @git_version ||= begin
408
- output = Git::Commands::Version.new(self).call.stdout
409
- Git::Version.parse(output)
425
+ call_opts = timeout.nil? ? {} : { timeout: timeout }
426
+ Git::Version.parse(Git::Commands::Version.new(self).call(**call_opts).stdout)
410
427
  end
411
428
  end
412
429
 
413
- private
414
-
415
430
  # Returns a Hash of environment variable overrides for this context
416
431
  #
417
432
  # Builds the standard git environment from the public accessor methods
@@ -424,6 +439,8 @@ module Git
424
439
  #
425
440
  # @return [Hash<String, String|nil>] the merged environment variable overrides
426
441
  #
442
+ # @api private
443
+ #
427
444
  def env_overrides(**additional_overrides)
428
445
  {
429
446
  'GIT_DIR' => git_dir,
@@ -435,6 +452,8 @@ module Git
435
452
  }.merge(additional_overrides)
436
453
  end
437
454
 
455
+ private
456
+
438
457
  # Returns the Array of git global option strings for this context
439
458
  #
440
459
  # Prepends `--git-dir` and `--work-tree` when the corresponding attributes
@@ -453,9 +472,9 @@ module Git
453
472
  # Creates a {Git::CommandLine::Capturing} instance for the current invocation.
454
473
  #
455
474
  # A new instance is created per call so that {#binary_path} — resolved from
456
- # `Git::Base.config` when set to `:use_global_config` — and {#env_overrides}
475
+ # `Git::Config.instance` when set to `:use_global_config` — and {#env_overrides}
457
476
  # — including {#git_ssh} resolution for `:use_global_config` — reflect the
458
- # state of `Git::Base.config` at the time of each command invocation.
477
+ # state of {Git::Config.instance} at the time of each command invocation.
459
478
  #
460
479
  # @return [Git::CommandLine::Capturing] the capturing command line instance
461
480
  #
@@ -466,9 +485,9 @@ module Git
466
485
  # Creates a {Git::CommandLine::Streaming} instance for the current invocation.
467
486
  #
468
487
  # A new instance is created per call so that {#binary_path} — resolved from
469
- # `Git::Base.config` when set to `:use_global_config` — and {#env_overrides}
488
+ # `Git::Config.instance` when set to `:use_global_config` — and {#env_overrides}
470
489
  # — including {#git_ssh} resolution for `:use_global_config` — reflect the
471
- # state of `Git::Base.config` at the time of each command invocation.
490
+ # state of {Git::Config.instance} at the time of each command invocation.
472
491
  #
473
492
  # @return [Git::CommandLine::Streaming] the streaming command line instance
474
493
  #
data/lib/git/log.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
  # Builds and executes a `git log` query
7
5
  #
@@ -44,7 +42,8 @@ module Git
44
42
  # git = Git.open('.')
45
43
  # Git::Log.new(git)
46
44
  #
47
- # @param base [Git::Repository, Git::Base] the git repository object
45
+ # @param base [Git::Repository] the git repository object
46
+ #
48
47
  # @param max_count [Integer, Symbol, nil] the number of commits to return, or
49
48
  # `:all` or `nil` to return all
50
49
  #
@@ -159,15 +158,10 @@ module Git
159
158
  self
160
159
  end
161
160
 
162
- # Returns the facade interface for log operations.
163
- #
164
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
165
- # The `is_a?` guard will be removed when {Git::Base} is deleted in Phase 4.
166
- #
167
161
  # @return [Git::Repository]
168
162
  #
169
163
  def log_repository
170
- @base.is_a?(Git::Base) ? @base.facade_repository : @base
164
+ @base
171
165
  end
172
166
 
173
167
  def run_log_if_dirty
data/lib/git/object.rb CHANGED
@@ -4,7 +4,6 @@ require 'git/author'
4
4
  require 'git/diff'
5
5
  require 'git/errors'
6
6
  require 'git/log'
7
- require 'git/base'
8
7
 
9
8
  module Git
10
9
  # represents a git object
@@ -103,7 +102,7 @@ module Git
103
102
  #
104
103
  # @param file [String, nil] destination file path; a temp file is created if `nil`
105
104
  #
106
- # @param opts [Hash] archive options (see {Git::Lib#archive})
105
+ # @param opts [Hash] archive options (see {Git::Repository#archive})
107
106
  #
108
107
  # @return [String] the path to the written archive file
109
108
  #
@@ -126,15 +125,10 @@ module Git
126
125
 
127
126
  private
128
127
 
129
- # Returns the facade interface for git object queries.
130
- #
131
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
132
- # The `is_a?` guard will be removed when {Git::Base} is deleted in Phase 4.
133
- #
134
128
  # @return [Git::Repository]
135
129
  #
136
130
  def object_repository
137
- @base.is_a?(Git::Base) ? @base.facade_repository : @base
131
+ @base
138
132
  end
139
133
  end
140
134
 
@@ -315,19 +309,23 @@ module Git
315
309
  attr_accessor :name
316
310
 
317
311
  # @overload initialize(base, name)
318
- # @param base [Git::Base] The Git base object
319
- # @param name [String] The name of the tag
312
+ #
313
+ # @param base [Git::Repository] the git repository
314
+ #
315
+ # @param name [String] the name of the tag
320
316
  #
321
317
  # @overload initialize(base, sha, name)
322
- # @param base [Git::Base] The Git base object
323
- # @param sha [String] The SHA of the tag object
324
- # @param name [String] The name of the tag
318
+ #
319
+ # @param base [Git::Repository] the git repository
320
+ #
321
+ # @param sha [String] the SHA of the tag object
322
+ #
323
+ # @param name [String] the name of the tag
325
324
  #
326
325
  def initialize(base, sha, name = nil)
327
326
  if name.nil?
328
327
  name = sha
329
- repo = base.is_a?(Git::Base) ? base.facade_repository : base
330
- sha = repo.tag_sha(name)
328
+ sha = base.tag_sha(name)
331
329
  raise Git::UnexpectedResultError, "Tag '#{name}' does not exist." if sha == ''
332
330
  end
333
331
 
@@ -394,15 +392,10 @@ module Git
394
392
  Git::Object::Tag.new(base, objectish)
395
393
  end
396
394
 
397
- # Returns the facade interface for git object queries.
398
- #
399
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
400
- # The `is_a?` guard will be removed when {Git::Base} is deleted in Phase 4.
401
- #
402
395
  # @return [Git::Repository]
403
396
  #
404
397
  private_class_method def self.object_repository_for(base)
405
- base.is_a?(Git::Base) ? base.facade_repository : base
398
+ base
406
399
  end
407
400
  end
408
401
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/config_entry_info'
4
+
5
+ module Git
6
+ module Parsers
7
+ # Parser for `git config` output
8
+ #
9
+ # Each public method corresponds to a different `git config` sub-command
10
+ # variant and handles the specific output format that variant produces.
11
+ # Most variants are called with `--show-scope --show-origin --null`;
12
+ # `parse_urlmatch` handles the `--get-urlmatch` format where `--show-origin`
13
+ # is unsupported and therefore absent.
14
+ #
15
+ # @api private
16
+ #
17
+ module ConfigEntry
18
+ module_function
19
+
20
+ # Parse `git config --get --show-scope --show-origin --null` output
21
+ #
22
+ # Output format (per entry): `scope\0origin\0value\0`
23
+ #
24
+ # The key name is absent from `--get` output and must be supplied by the
25
+ # caller.
26
+ #
27
+ # @param key [String] the config key name that was queried
28
+ #
29
+ # @param output [String] raw stdout from the command
30
+ #
31
+ # @return [Git::ConfigEntryInfo, nil] the parsed entry, or `nil` when not found
32
+ #
33
+ def parse_get(key, output)
34
+ return nil if output.empty?
35
+
36
+ scope, origin, value = output.split("\0", -1)
37
+ Git::ConfigEntryInfo.new(scope: scope, origin: origin, key: key, value: value)
38
+ end
39
+
40
+ # Parse `git config --get-all --show-scope --show-origin --null` output
41
+ #
42
+ # Output format (per entry): `scope\0origin\0value\0`
43
+ #
44
+ # The key name is absent from `--get-all` output and must be supplied by
45
+ # the caller. Multiple entries appear back-to-back in the same string.
46
+ #
47
+ # @param key [String] the config key name that was queried
48
+ #
49
+ # @param output [String] raw stdout from the command
50
+ #
51
+ # @return [Array<Git::ConfigEntryInfo>] the parsed entries
52
+ #
53
+ def parse_get_all(key, output)
54
+ return [] if output.empty?
55
+
56
+ tokens = output.split("\0", -1)
57
+ tokens.pop if tokens.last == ''
58
+ tokens.each_slice(3).map do |scope, origin, value|
59
+ Git::ConfigEntryInfo.new(scope: scope, origin: origin, key: key, value: value)
60
+ end
61
+ end
62
+
63
+ # Parse `git config --list --show-scope --show-origin --null` output
64
+ #
65
+ # Also handles `--get-regexp` output, which shares the same format.
66
+ #
67
+ # Output format (per entry): `scope\0origin\0key\nvalue\0`
68
+ #
69
+ # Multiple entries appear back-to-back in the same string.
70
+ #
71
+ # @param output [String] raw stdout from the command
72
+ #
73
+ # @return [Array<Git::ConfigEntryInfo>] the parsed entries
74
+ #
75
+ def parse_list(output)
76
+ return [] if output.empty?
77
+
78
+ tokens = output.split("\0", -1)
79
+ tokens.pop if tokens.last == ''
80
+ tokens.each_slice(3).map do |scope, origin, key_value|
81
+ key, value = key_value.split("\n", 2)
82
+ Git::ConfigEntryInfo.new(scope: scope, origin: origin, key: key, value: value || '')
83
+ end
84
+ end
85
+
86
+ # Parse `git config --get-urlmatch --show-scope --null` output
87
+ #
88
+ # Handles the two-field-per-entry format produced by `--get-urlmatch` when
89
+ # `--show-scope` is used but `--show-origin` is not (git does not support
90
+ # `--show-origin` with `--get-urlmatch`).
91
+ #
92
+ # Output format (per entry): `scope\0key\nvalue\0`
93
+ #
94
+ # @param output [String] raw stdout from the command
95
+ #
96
+ # @return [Array<Git::ConfigEntryInfo>] the parsed entries; `origin` is `nil` for each
97
+ #
98
+ def parse_urlmatch(output)
99
+ return [] if output.empty?
100
+
101
+ tokens = output.split("\0", -1)
102
+ tokens.pop if tokens.last == ''
103
+ tokens.each_slice(2).map do |scope, key_value|
104
+ key, value = key_value.split("\n", 2)
105
+ Git::ConfigEntryInfo.new(scope: scope, origin: nil, key: key, value: value || '')
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end