git 5.0.0.beta.1 → 5.0.0.beta.3

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 +90 -22
  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/context_helpers.rb +264 -0
  44. data/lib/git/repository/factories.rb +682 -0
  45. data/lib/git/repository/inspecting.rb +99 -0
  46. data/lib/git/repository/maintenance.rb +65 -0
  47. data/lib/git/repository/merging.rb +63 -1
  48. data/lib/git/repository/object_operations.rb +133 -35
  49. data/lib/git/repository/path_resolver.rb +1 -1
  50. data/lib/git/repository/remote_operations.rb +166 -21
  51. data/lib/git/repository/staging.rb +187 -23
  52. data/lib/git/repository/stashing.rb +39 -3
  53. data/lib/git/repository/status_operations.rb +21 -0
  54. data/lib/git/repository.rb +288 -128
  55. data/lib/git/stash.rb +2 -9
  56. data/lib/git/stashes.rb +2 -7
  57. data/lib/git/status.rb +12 -23
  58. data/lib/git/version.rb +2 -2
  59. data/lib/git/worktree.rb +2 -15
  60. data/lib/git/worktrees.rb +2 -15
  61. data/lib/git.rb +182 -77
  62. data/redesign/3_architecture_implementation.md +170 -112
  63. data/redesign/Phase 4 - Step A.md +366 -0
  64. data/redesign/beta_release.md +107 -0
  65. data/redesign/c1c2_audit.md +566 -0
  66. data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
  67. data/redesign/config_design.rb +501 -0
  68. metadata +19 -6
  69. data/lib/git/base.rb +0 -1204
  70. data/lib/git/lib.rb +0 -2855
  71. data/lib/git/repository/configuring.rb +0 -156
@@ -3,17 +3,21 @@
3
3
  require 'find'
4
4
  require 'pathname'
5
5
 
6
+ require 'git/configuring'
7
+ require 'git/deprecation'
6
8
  require 'git/execution_context/repository'
7
9
  require 'git/repository/branching'
10
+ require 'git/repository/context_helpers'
8
11
  require 'git/repository/committing'
9
- require 'git/repository/configuring'
10
12
  require 'git/repository/diffing'
13
+ require 'git/repository/factories'
11
14
  require 'git/repository/inspecting'
12
15
  require 'git/repository/logging'
16
+ require 'git/repository/maintenance'
13
17
  require 'git/repository/merging'
14
18
  require 'git/repository/object_operations'
15
- require 'git/repository/path_resolver'
16
19
  require 'git/repository/remote_operations'
20
+ require 'git/repository/shared_private'
17
21
  require 'git/repository/staging'
18
22
  require 'git/repository/stashing'
19
23
  require 'git/repository/status_operations'
@@ -44,13 +48,17 @@ module Git
44
48
  #
45
49
  # @api public
46
50
  #
47
- class Repository
51
+ class Repository # rubocop:disable Metrics/ClassLength
52
+ extend Git::Repository::Factories
53
+
54
+ include Git::Configuring
48
55
  include Git::Repository::Branching
56
+ include Git::Repository::ContextHelpers
49
57
  include Git::Repository::Committing
50
- include Git::Repository::Configuring
51
58
  include Git::Repository::Diffing
52
59
  include Git::Repository::Inspecting
53
60
  include Git::Repository::Logging
61
+ include Git::Repository::Maintenance
54
62
  include Git::Repository::Merging
55
63
  include Git::Repository::ObjectOperations
56
64
  include Git::Repository::RemoteOperations
@@ -59,133 +67,22 @@ module Git
59
67
  include Git::Repository::StatusOperations
60
68
  include Git::Repository::WorktreeOperations
61
69
 
62
- # Open a working copy at an existing path
63
- #
64
- # The new repository factories are additive scaffolding introduced by the
65
- # architectural redesign. The top-level {Git.open} entry point still returns a
66
- # {Git::Base} object; this method exists so future work can route construction
67
- # through {Git::Repository} without changing the public entry points.
68
- #
69
- # Note: this method opens working copies only. To open a bare repository, use
70
- # {Git::Repository.bare}.
71
- #
72
- # @example Open the working copy in the current directory
73
- # repository = Git::Repository.open('.')
74
- #
75
- # @param working_dir [String] the path to the root of the working copy
76
- #
77
- # May be any path inside the working tree when `:repository` is not given.
78
- #
79
- # @param options [Hash] options that control how the repository is located
80
- #
81
- # @option options [String] :repository a non-standard path to the `.git`
82
- # directory
83
- #
84
- # When given, `working_dir` is used as-is (the working tree root is not
85
- # auto-detected).
86
- #
87
- # @option options [String] :index a non-standard path to the index file
88
- #
89
- # @option options [Logger] :log a logger forwarded to the command layer
90
- #
91
- # @option options [String, nil, :use_global_config] :git_ssh path to a custom SSH executable;
92
- # pass `:use_global_config` (the default) to use `Git::Base.config.git_ssh`
93
- #
94
- # @option options [String, :use_global_config] :binary_path path to the git binary;
95
- # pass `:use_global_config` (the default) to use `Git::Base.config.binary_path`
96
- #
97
- # @return [Git::Repository] a repository bound to the resolved paths
98
- #
99
- # @raise [ArgumentError] if `working_dir` is not a directory or is not inside
100
- # a git working tree
101
- #
102
- def self.open(working_dir, options = {})
103
- raise ArgumentError, "'#{working_dir}' is not a directory" unless Dir.exist?(working_dir)
104
-
105
- working_dir = resolve_open_working_dir(working_dir, options) unless options[:repository]
70
+ CONFIG_SET_ALLOWED_OPTS = %i[file].freeze
71
+ private_constant :CONFIG_SET_ALLOWED_OPTS
106
72
 
107
- paths = PathResolver.resolve_paths(
108
- working_directory: working_dir,
109
- repository: options[:repository],
110
- index: options[:index]
111
- )
112
-
113
- from_paths(options, paths)
114
- end
73
+ CONFIG_READ_ALLOWED_OPTS = %i[file].freeze
74
+ private_constant :CONFIG_READ_ALLOWED_OPTS
115
75
 
116
- # Open an existing bare repository at `git_dir`
117
- #
118
- # The new repository factories are additive scaffolding introduced by the
119
- # architectural redesign. The top-level {Git.bare} entry point still returns a
120
- # {Git::Base} object; this method exists so future work can route construction
121
- # through {Git::Repository} without changing the public entry points.
122
- #
123
- # @example Open a bare repository
124
- # repository = Git::Repository.bare('/path/to/repo.git')
125
- #
126
- # @param git_dir [String] the path to the bare repository directory
127
- #
128
- # @param options [Hash] options forwarded to the constructed repository
129
- #
130
- # @option options [Logger] :log a logger forwarded to the command layer
131
- #
132
- # @option options [String, nil, :use_global_config] :git_ssh path to a custom SSH executable;
133
- # pass `:use_global_config` (the default) to use `Git::Base.config.git_ssh`
134
- #
135
- # @option options [String, :use_global_config] :binary_path path to the git binary;
136
- # pass `:use_global_config` (the default) to use `Git::Base.config.binary_path`
137
- #
138
- # @return [Git::Repository] a repository bound to the bare repository directory
139
- #
140
- def self.bare(git_dir, options = {})
141
- paths = PathResolver.resolve_paths(repository: git_dir, bare: true)
142
-
143
- from_paths(options, paths)
144
- end
145
-
146
- # Resolve the worktree root to use as the working directory for `.open`
147
- #
148
- # Delegates to {PathResolver.root_of_worktree}, forwarding `:binary_path`
149
- # and `:git_ssh` from `options`.
150
- #
151
- # @param working_dir [String] a path inside the working tree
152
- #
153
- # @param options [Hash] the caller-supplied options hash from `.open`
154
- #
155
- # @return [String] the absolute path to the root of the working tree
156
- #
157
- # @raise [ArgumentError] if `working_dir` is not inside a git working tree
158
- #
159
- # @api private
160
- #
161
- def self.resolve_open_working_dir(working_dir, options)
162
- PathResolver.root_of_worktree(
163
- working_dir,
164
- binary_path: options.fetch(:binary_path, :use_global_config),
165
- git_ssh: options.fetch(:git_ssh, :use_global_config)
166
- )
167
- end
168
- private_class_method :resolve_open_working_dir
76
+ CONFIG_DEPRECATION_WARNING =
77
+ 'Git::Repository#config is deprecated and will be removed in v6.0.0. ' \
78
+ 'Use config_get(name), config_set(name, value), or config_list instead.'
79
+ private_constant :CONFIG_DEPRECATION_WARNING
169
80
 
170
- # Build a repository from caller options and resolved paths
171
- #
172
- # @param options [Hash] the caller-supplied options (`:git_ssh`,
173
- # `:binary_path`, `:log`)
174
- #
175
- # @param paths [Hash{Symbol => (String, nil)}] the resolved
176
- # `:working_directory`, `:repository`, and `:index` paths
177
- #
178
- # @return [Git::Repository] the constructed repository
179
- #
180
- # @api private
181
- #
182
- def self.from_paths(options, paths)
183
- execution_context = Git::ExecutionContext::Repository.from_hash(
184
- options.merge(paths), logger: options[:log]
185
- )
186
- new(execution_context: execution_context)
187
- end
188
- private_class_method :from_paths
81
+ GLOBAL_CONFIG_DEPRECATION_WARNING =
82
+ 'Git::Repository#global_config is deprecated and will be removed in v6.0.0. ' \
83
+ 'Use config_get(name, global: true), config_set(name, value, global: true), ' \
84
+ 'or config_list(global: true) instead.'
85
+ private_constant :GLOBAL_CONFIG_DEPRECATION_WARNING
189
86
 
190
87
  # @return [Git::ExecutionContext::Repository] the execution context used to run
191
88
  # git commands for this repository
@@ -239,6 +136,202 @@ module Git
239
136
  index_file && Pathname.new(index_file)
240
137
  end
241
138
 
139
+ # Returns `self` after emitting a deprecation warning.
140
+ #
141
+ # Legacy callers that used `git.lib.some_method` can migrate to calling the
142
+ # facade method directly on the repository object. This shim will be removed
143
+ # in v6.0.0.
144
+ #
145
+ # @return [self]
146
+ #
147
+ # @api private
148
+ #
149
+ def lib
150
+ Git::Deprecation.warn(
151
+ 'Git::Repository#lib is deprecated and will be removed in v6.0.0. ' \
152
+ 'Use the repository object directly.'
153
+ )
154
+ self
155
+ end
156
+
157
+ # @return [String, nil] the git directory path
158
+ #
159
+ # @api private
160
+ def git_dir = execution_context.git_dir
161
+
162
+ # @return [String, nil] the working directory path
163
+ #
164
+ # @api private
165
+ def git_work_dir = execution_context.git_work_dir
166
+
167
+ # @return [String, nil] the index file path
168
+ #
169
+ # @api private
170
+ def git_index_file = execution_context.git_index_file
171
+
172
+ # @return [Git::Version] the installed git version
173
+ #
174
+ # @api private
175
+ def git_version(timeout: nil) = execution_context.git_version(timeout: timeout)
176
+
177
+ # @return [String, nil] the SSH wrapper path
178
+ #
179
+ # @api private
180
+ def git_ssh = execution_context.git_ssh
181
+
182
+ # @return [String, :use_global_config] the path to the git binary
183
+ #
184
+ # @api private
185
+ def binary_path = execution_context.binary_path
186
+
187
+ # Read or write a git configuration entry
188
+ #
189
+ # Dispatches to one of three modes depending on the arguments supplied:
190
+ #
191
+ # * **List** — `config()` returns all visible config entries as a `Hash`.
192
+ # * **Get** — `config(name)` returns the value for a single key as a `String`.
193
+ # * **Set** — `config(name, value)` writes a value and returns the raw
194
+ # command result.
195
+ #
196
+ # @overload config(options = {})
197
+ #
198
+ # @example List all config entries
199
+ # repo.config #=> { "user.name" => "Alice", "core.bare" => "false" }
200
+ #
201
+ # @example List all entries from a custom config file
202
+ # repo.config(file: '/path/to/.gitconfig')
203
+ # #=> { "user.name" => "Alice", "core.bare" => "false" }
204
+ #
205
+ # @param options [Hash] options for the list operation
206
+ #
207
+ # @option options [String, nil] :file (nil) path to a custom config file
208
+ # to read from instead of the default resolution chain
209
+ #
210
+ # @return [Hash{String => String}] all visible config entries, keyed by
211
+ # their full dotted key names (e.g. `"user.name"`)
212
+ #
213
+ # @raise [ArgumentError] if unsupported options are provided
214
+ #
215
+ # @overload config(name, options = {})
216
+ #
217
+ # @example Read the committer name from config
218
+ # repo.config('user.name') #=> "Alice"
219
+ #
220
+ # @example Read a value from a custom config file
221
+ # repo.config('user.name', file: '/path/to/.gitconfig') #=> "Alice"
222
+ #
223
+ # @param name [String] the dotted config key to look up (e.g.
224
+ # `"user.name"`)
225
+ #
226
+ # @param options [Hash] options for the get operation
227
+ #
228
+ # @option options [String, nil] :file (nil) path to a custom config file
229
+ # to read from instead of the default resolution chain
230
+ #
231
+ # @return [String] the value of the config entry
232
+ #
233
+ # @raise [ArgumentError] if unsupported options are provided
234
+ #
235
+ # @overload config(name, value, options = {})
236
+ #
237
+ # @example Set the committer name in local config
238
+ # repo.config('user.name', 'Alice')
239
+ #
240
+ # @example Write a value to a custom config file
241
+ # repo.config('user.name', 'Alice', file: '/path/to/custom/config')
242
+ #
243
+ # @param name [String] the dotted config key to write (e.g.
244
+ # `"user.name"`)
245
+ #
246
+ # @param value [#to_s] the value to assign; must not be `nil` (a `nil`
247
+ # value is treated as "no value" and routes to the get overload).
248
+ # Must not be a `Hash` (a Hash is treated as the `options` argument;
249
+ # call `value.to_s` explicitly before passing if a stringified Hash
250
+ # is genuinely needed). Any other non-nil object is converted to a
251
+ # String via `#to_s` before being passed to git
252
+ #
253
+ # @param options [Hash] options for the set operation
254
+ #
255
+ # @option options [String, nil] :file (nil) path to a custom config file
256
+ # to write to instead of the repository's default `.git/config`
257
+ #
258
+ # @return [Git::CommandLineResult] the raw result of
259
+ # `git config <name> <value>`
260
+ #
261
+ # @raise [ArgumentError] if unsupported options are provided
262
+ #
263
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
264
+ #
265
+ def config(name = nil, value = nil, options = {})
266
+ Git::Deprecation.warn(CONFIG_DEPRECATION_WARNING)
267
+ name, value, options = deprecated_normalize_config_args(name, value, options)
268
+
269
+ if !name.nil? && !value.nil?
270
+ deprecated_config_set(name, value, **options)
271
+ elsif name
272
+ deprecated_config_get(name, **options)
273
+ else
274
+ deprecated_config_list(**options)
275
+ end
276
+ end
277
+
278
+ # Read or write a global git configuration entry
279
+ #
280
+ # Dispatches to one of three modes depending on the arguments supplied,
281
+ # targeting the git global config scope (`git config --global`):
282
+ #
283
+ # * **List** — `global_config()` returns all global config entries as a `Hash`.
284
+ # * **Get** — `global_config(name)` returns the value for a single key as a `String`.
285
+ # * **Set** — `global_config(name, value)` writes a value and returns the raw
286
+ # command result.
287
+ #
288
+ # @overload global_config
289
+ #
290
+ # @example List all global config entries
291
+ # repo.global_config #=> { "user.name" => "Alice", "core.autocrlf" => "false" }
292
+ #
293
+ # @return [Hash{String => String}] all global config entries, keyed by their
294
+ # full dotted key names (e.g. `"user.name"`)
295
+ #
296
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
297
+ #
298
+ # @overload global_config(name)
299
+ #
300
+ # @example Read the global committer name
301
+ # repo.global_config('user.name') #=> "Alice"
302
+ #
303
+ # @param name [String] the dotted config key to look up (e.g. `"user.name"`)
304
+ #
305
+ # @return [String] the value of the global config entry
306
+ #
307
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
308
+ #
309
+ # @overload global_config(name, value)
310
+ #
311
+ # @example Set the global committer name
312
+ # repo.global_config('user.name', 'Alice')
313
+ #
314
+ # @param name [String] the dotted config key to write (e.g. `"user.name"`)
315
+ #
316
+ # @param value [#to_s] the value to assign; any object is accepted and
317
+ # converted to a String via `#to_s` before being passed to git
318
+ #
319
+ # @return [Git::CommandLineResult] the raw result of
320
+ # `git config --global <name> <value>`
321
+ #
322
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
323
+ #
324
+ def global_config(name = nil, value = nil)
325
+ Git::Deprecation.warn(GLOBAL_CONFIG_DEPRECATION_WARNING)
326
+ if !name.nil? && !value.nil?
327
+ deprecated_global_config_set(name, value)
328
+ elsif !name.nil?
329
+ deprecated_global_config_get(name)
330
+ else
331
+ deprecated_global_config_list
332
+ end
333
+ end
334
+
242
335
  # Returns the size of the repository directory in bytes
243
336
  #
244
337
  # Sums the sizes of every regular file under the repository (`.git`)
@@ -265,5 +358,72 @@ module Git
265
358
  end
266
359
  total
267
360
  end
361
+
362
+ private
363
+
364
+ def deprecated_normalize_config_args(name, value, options)
365
+ if name.is_a?(Hash)
366
+ raise ArgumentError, 'unexpected positional arguments after options hash' if !value.nil? || !options.empty?
367
+
368
+ [nil, nil, name]
369
+ elsif value.is_a?(Hash)
370
+ raise ArgumentError, 'unexpected third argument when second argument is options hash' unless options.empty?
371
+
372
+ [name, nil, value]
373
+ else
374
+ [name, value, options]
375
+ end
376
+ end
377
+
378
+ def deprecated_config_set(name, value, **)
379
+ SharedPrivate.assert_valid_opts!(CONFIG_SET_ALLOWED_OPTS, **)
380
+ Git::Commands::ConfigOptionSyntax::Set.new(@execution_context).call(name, value, **)
381
+ end
382
+
383
+ def deprecated_config_get(name, **options)
384
+ SharedPrivate.assert_valid_opts!(CONFIG_READ_ALLOWED_OPTS, **options)
385
+ opts = options[:file] ? { file: options[:file] } : {}
386
+ result = Git::Commands::ConfigOptionSyntax::Get.new(@execution_context).call(name, **opts)
387
+ raise Git::FailedError, result if result.status.exitstatus != 0
388
+
389
+ result.stdout
390
+ end
391
+
392
+ def deprecated_config_list(**options)
393
+ SharedPrivate.assert_valid_opts!(CONFIG_READ_ALLOWED_OPTS, **options)
394
+ opts = options[:file] ? { file: options[:file] } : {}
395
+ lines = Git::Commands::ConfigOptionSyntax::List.new(@execution_context).call(**opts).stdout.split("\n")
396
+ lines.each_with_object({}) do |line, hsh|
397
+ key, value = line.split('=', 2)
398
+ hsh[key] = value || ''
399
+ end
400
+ end
401
+
402
+ def deprecated_global_config_get(name)
403
+ result = Git::Commands::ConfigOptionSyntax::Get.new(@execution_context).call(name, global: true)
404
+ raise Git::FailedError, result if result.status.exitstatus != 0
405
+
406
+ result.stdout
407
+ end
408
+
409
+ def deprecated_global_config_list
410
+ lines = Git::Commands::ConfigOptionSyntax::List.new(@execution_context).call(global: true).stdout.split("\n")
411
+ lines.each_with_object({}) do |line, hsh|
412
+ key, value = line.split('=', 2)
413
+ hsh[key] = value || ''
414
+ end
415
+ end
416
+
417
+ def deprecated_global_config_set(name, value)
418
+ Git::Commands::ConfigOptionSyntax::Set.new(@execution_context).call(name, value, global: true)
419
+ end
420
+
421
+ # All git config scopes are valid in a repository context
422
+ #
423
+ # @return [void]
424
+ #
425
+ def assert_valid_scope!(**)
426
+ nil
427
+ end
268
428
  end
269
429
  end
data/lib/git/stash.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
  # Represents a single stash entry in a Git repository
7
5
  #
@@ -18,7 +16,7 @@ module Git
18
16
  # When `existing` is `false` (the default), immediately calls {#save} to push
19
17
  # the current working-directory state onto the stash stack.
20
18
  #
21
- # @param base [Git::Repository, Git::Base] the git repository
19
+ # @param base [Git::Repository] the git repository
22
20
  #
23
21
  # @param message [String] the stash message
24
22
  #
@@ -93,15 +91,10 @@ module Git
93
91
 
94
92
  private
95
93
 
96
- # Returns the facade interface for stash operations
97
- #
98
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
99
- # The `is_a?` guard will be removed when {Git::Base} is deleted in Phase 4.
100
- #
101
94
  # @return [Git::Repository]
102
95
  #
103
96
  def stash_repository
104
- @base.is_a?(Git::Base) ? @base.facade_repository : @base
97
+ @base
105
98
  end
106
99
  end
107
100
  end
data/lib/git/stashes.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 stash entries for a Git repository
7
5
  #
@@ -21,7 +19,7 @@ module Git
21
19
  #
22
20
  # Loads all existing stash entries from the repository at construction time.
23
21
  #
24
- # @param base [Git::Repository, Git::Base] the git repository
22
+ # @param base [Git::Repository] the git repository
25
23
  #
26
24
  # @return [void]
27
25
  #
@@ -161,13 +159,10 @@ module Git
161
159
 
162
160
  # Returns the facade interface for stash operations
163
161
  #
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
162
  # @return [Git::Repository]
168
163
  #
169
164
  def stash_repository
170
- @base.is_a?(Git::Base) ? @base.facade_repository : @base
165
+ @base
171
166
  end
172
167
  end
173
168
  end
data/lib/git/status.rb CHANGED
@@ -14,7 +14,7 @@ module Git
14
14
 
15
15
  # Create a new Status for the given repository
16
16
  #
17
- # @param base [Git::Base, Git::Repository] the git object backing this status
17
+ # @param base [Git::Repository] the git object backing this status
18
18
  #
19
19
  def initialize(base)
20
20
  @base = base
@@ -172,17 +172,15 @@ module Git
172
172
 
173
173
  # Return `true` when git is configured to ignore filename case
174
174
  #
175
- # Reads `core.ignoreCase` from the repository config. Returns `false` if
176
- # the config value is absent or if reading it raises {Git::FailedError}.
175
+ # Reads `core.ignoreCase` with {Git::Repository#config_get}. Returns `false`
176
+ # when the config value is absent.
177
177
  #
178
178
  # @return [Boolean] `true` when `core.ignoreCase` is `"true"`
179
179
  #
180
180
  def ignore_case?
181
- return @_ignore_case if defined?(@_ignore_case)
181
+ return @ignore_case if defined?(@ignore_case)
182
182
 
183
- @_ignore_case = (@base.config('core.ignoreCase') == 'true')
184
- rescue Git::FailedError
185
- @_ignore_case = false
183
+ @ignore_case = (@base.config_get('core.ignoreCase')&.value == 'true')
186
184
  end
187
185
  end
188
186
  end
@@ -239,7 +237,7 @@ module Git
239
237
 
240
238
  # Initialize a new StatusFile with the given git object and data hash
241
239
  #
242
- # @param base [Git::Base, Git::Repository] the git object
240
+ # @param base [Git::Repository] the git object
243
241
  #
244
242
  # @param hash [Hash] raw status data for this file
245
243
  #
@@ -281,18 +279,10 @@ module Git
281
279
  class StatusFileFactory
282
280
  # Create a new factory backed by the given git object
283
281
  #
284
- # When `base` is a {Git::Repository} (which exposes `#diff_index`
285
- # directly), it is used as the data provider. When `base` is a
286
- # {Git::Base} (the legacy path), `base.lib` is used instead.
287
- #
288
- # @param base [Git::Base, Git::Repository] the git object used as the
289
- # status data provider
282
+ # @param base [Git::Repository] the git object used as the status data provider
290
283
  #
291
284
  def initialize(base)
292
285
  @base = base
293
- # When base is Git::Repository (which exposes #diff_index directly),
294
- # use it as the data provider. Otherwise use base.lib (legacy path).
295
- @provider = base.respond_to?(:diff_index) ? base : base.lib
296
286
  end
297
287
 
298
288
  # Gather all status data and build a hash of file paths to StatusFile objects
@@ -314,7 +304,7 @@ module Git
314
304
  # @return [Hash{String => Hash}] raw per-file status data keyed by path
315
305
  #
316
306
  def fetch_all_files_data
317
- files = @provider.ls_files # Start with files tracked in the index.
307
+ files = @base.ls_files # Start with files tracked in the index.
318
308
  merge_untracked_files(files)
319
309
  merge_modified_files(files)
320
310
  merge_head_diffs(files)
@@ -328,7 +318,7 @@ module Git
328
318
  # @return [void]
329
319
  #
330
320
  def merge_untracked_files(files)
331
- @provider.untracked_files.each do |file|
321
+ @base.untracked_files.each do |file|
332
322
  files[file] = { path: file, untracked: true }
333
323
  end
334
324
  end
@@ -341,7 +331,7 @@ module Git
341
331
  #
342
332
  def merge_modified_files(files)
343
333
  # Merge changes between the index and the working directory.
344
- @provider.diff_files.each do |path, data|
334
+ @base.diff_files.each do |path, data|
345
335
  (files[path] ||= {}).merge!(data)
346
336
  end
347
337
  end
@@ -353,12 +343,11 @@ module Git
353
343
  # @return [void]
354
344
  #
355
345
  def merge_head_diffs(files)
356
- # Git::Repository exposes #no_commits?; Git::Lib exposes #empty?.
357
- is_empty = @provider.respond_to?(:no_commits?) ? @provider.no_commits? : @provider.empty?
346
+ is_empty = @base.no_commits?
358
347
  return if is_empty
359
348
 
360
349
  # Merge changes between HEAD and the index.
361
- @provider.diff_index('HEAD').each do |path, data|
350
+ @base.diff_index('HEAD').each do |path, data|
362
351
  (files[path] ||= {}).merge!(data)
363
352
  end
364
353
  end
data/lib/git/version.rb CHANGED
@@ -4,7 +4,7 @@ module Git
4
4
  # The current gem version
5
5
  #
6
6
  # @return [String] the current gem version
7
- VERSION = '5.0.0.beta.1'
7
+ VERSION = '5.0.0.beta.3'
8
8
 
9
9
  # Represents a git version with major, minor, and patch components
10
10
  #
@@ -100,7 +100,7 @@ module Git
100
100
  # Return the version as an array of integers
101
101
  #
102
102
  # Useful when legacy code expects the array shape returned by the
103
- # deprecated {Git::Lib#current_command_version} method.
103
+ # deprecated `Git::Lib#current_command_version` method.
104
104
  #
105
105
  # @return [Array<Integer>] [major, minor, patch]
106
106
  #
data/lib/git/worktree.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
  # A worktree in a Git repository
7
5
  #
@@ -9,11 +7,6 @@ module Git
9
7
  # {Git::Repository::WorktreeOperations#worktree} or populated by
10
8
  # {Git::Worktrees}.
11
9
  #
12
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy form)
13
- # as the `base` argument. The `is_a?(Git::Base)` guard routes git operations
14
- # through the facade repository and will be removed when {Git::Base} is
15
- # deleted in Phase 4.
16
- #
17
10
  # @example Add and remove a linked worktree
18
11
  # worktree = repo.worktree('/path/to/new-worktree')
19
12
  # worktree.add
@@ -37,7 +30,7 @@ module Git
37
30
 
38
31
  # Creates a new Worktree object
39
32
  #
40
- # @param base [Git::Base, Git::Repository] the repository that owns this
33
+ # @param base [Git::Repository] the repository that owns this
41
34
  # worktree
42
35
  #
43
36
  # @param dir [String] filesystem path of the worktree
@@ -137,18 +130,12 @@ module Git
137
130
 
138
131
  private
139
132
 
140
- # Resolves the {Git::Repository} for worktree operations
141
- #
142
- # Accepts either a {Git::Repository} (new form) or a {Git::Base} (legacy).
143
- # The `is_a?(Git::Base)` guard will be removed when {Git::Base} is deleted
144
- # in Phase 4.
145
- #
146
133
  # @return [Git::Repository] the repository used for worktree operations
147
134
  #
148
135
  # @api private
149
136
  #
150
137
  def worktree_repository
151
- @base.is_a?(Git::Base) ? @base.facade_repository : @base
138
+ @base
152
139
  end
153
140
  end
154
141
  end