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
@@ -9,11 +9,12 @@ risk and allows for a gradual, controlled migration to the new architecture.
9
9
  - [Facade Modules Completed](#facade-modules-completed)
10
10
  - [Facade module naming convention](#facade-module-naming-convention)
11
11
  - [Next Task](#next-task)
12
- - [Phase 3 Public Interface Completion](#phase-3-public-interface-completion)
12
+ - [D2 / Phase 4 is the remaining redesign step](#d2--phase-4-is-the-remaining-redesign-step)
13
+ - [Phase 3 Overview](#phase-3-overview)
13
14
  - [Workstream A — Fill facade coverage gaps](#workstream-a--fill-facade-coverage-gaps)
14
15
  - [Workstream B — C0: Redirect `Git::Base` factory methods to `facade_repository`](#workstream-b--c0-redirect-gitbase-factory-methods-to-facade_repository)
15
16
  - [Workstream C — C1: Prepare and flip top-level entry points to return `Git::Repository`](#workstream-c--c1-prepare-and-flip-top-level-entry-points-to-return-gitrepository)
16
- - [Workstream D — C2+C3: Remove compatibility bridges](#workstream-d--c2c3-remove-compatibility-bridges)
17
+ - [Workstream D — C3: Remove compatibility fallbacks](#workstream-d--c3-remove-compatibility-fallbacks)
17
18
  - [Workstream E — Migrate or deprecate instance helper methods](#workstream-e--migrate-or-deprecate-instance-helper-methods)
18
19
  - [Workstream F — `Git` module utility methods still using `Git::Lib` directly](#workstream-f--git-module-utility-methods-still-using-gitlib-directly)
19
20
  - [Phase 3 dependency order](#phase-3-dependency-order)
@@ -31,6 +32,10 @@ risk and allows for a gradual, controlled migration to the new architecture.
31
32
  - [⏳ Commands To Migrate](#-commands-to-migrate)
32
33
  - [Phase 3: Refactoring the Public Interface](#phase-3-refactoring-the-public-interface)
33
34
  - [Phase 4: Final Cleanup and Release Preparation](#phase-4-final-cleanup-and-release-preparation)
35
+ - [Phase 4 step graph](#phase-4-step-graph)
36
+ - [Step A — Remove old code](#step-a--remove-old-code)
37
+ - [Step B — Finalize test suite](#step-b--finalize-test-suite)
38
+ - [Step C — Update documentation](#step-c--update-documentation)
34
39
 
35
40
  ## Progress Tracker
36
41
 
@@ -38,44 +43,80 @@ risk and allows for a gradual, controlled migration to the new architecture.
38
43
  | ----- | ------ | ----------- | :--------------: | :--------------: |
39
44
  | Phase 1 | ✅ Complete | Foundation and scaffolding | 5% | 100% |
40
45
  | Phase 2 | ✅ Complete | Migrating commands (all checklist items done) | 40% | 100% |
41
- | Phase 3 | In Progress | Refactoring public interface — see [Facade Modules Completed](#facade-modules-completed) and [Facade coverage checklist](#facade-coverage-checklist) | 45% | 50% |
42
- | Phase 4 | 🔲 Not Started | Final cleanup and release | 10% | 0% |
43
- | **TOTAL** | -- | -- | **100%** | **68%** |
46
+ | Phase 3 | Complete | Refactoring public interface — see [Facade Modules Completed](#facade-modules-completed) and [Facade coverage checklist](#facade-coverage-checklist) | 45% | 100% |
47
+ | Phase 4 | 🚧 In Progress | Final cleanup and release — Step A complete; Steps B and C remain | 10% | 33% |
48
+ | **TOTAL** | -- | -- | **100%** | **93%** |
44
49
 
45
50
  ### Facade Modules Completed
46
51
 
47
52
  | Module | File | Included in `Git::Repository` | `Git::Base` delegates |
48
53
  | ------ | ---- | ------------------------------ | --------------------- |
49
- | `Git::Repository::Staging` | `lib/git/repository/staging.rb` | ✅ | `add`, `reset`, `rm`, `clean`, `ignored_files` |
54
+ | `Git::Repository::Staging` | `lib/git/repository/staging.rb` | ✅ | `add`, `reset`, `reset_hard`, `apply`, `apply_mail`, `read_tree`, `rm`, `mv`, `clean`, `ignored_files` |
50
55
  | `Git::Repository::Committing` | `lib/git/repository/committing.rb` | ✅ | `commit`, `commit_all`, `write_tree`; `commit_tree` and `write_and_commit_tree` wrap the SHA result in `Git::Object::Commit.new(self, ...)` |
51
- | `Git::Repository::Branching` | `lib/git/repository/branching.rb` | ✅ | `checkout`, `checkout_file`, `checkout_index`, `current_branch`, `local_branch?`, `remote_branch?`, `branch?`, `branch_delete`, `branch_new`, `branch_contains`, `branches_all`, `update_ref` |
56
+ | `Git::Repository::Branching` | `lib/git/repository/branching.rb` | ✅ | `checkout`, `checkout_file`, `checkout_index`, `current_branch`, `current_branch_state`, `local_branch?`, `remote_branch?`, `branch?`, `branch`, `branches`, `branch_delete`, `branch_new`, `change_head_branch`, `branch_contains`, `branches_all`, `update_ref` |
57
+ | `Git::Repository::ContextHelpers` | `lib/git/repository/context_helpers.rb` | ✅ | `chdir`, `with_index`, `with_temp_index`, `with_working`, `with_temp_working`, `set_index`, `set_working` |
52
58
  | `Git::Repository::Merging` | `lib/git/repository/merging.rb` | ✅ | `merge`, `revert`, `each_conflict`; `merge_base` wraps the returned SHA strings in `Git::Object::Commit.new(self, ...)` instances |
53
- | `Git::Repository::RemoteOperations` | `lib/git/repository/remote_operations.rb` | ✅ | `fetch`, `pull`, `push`, `add_remote`, `remove_remote`, `config_remote`, `remotes`, `set_remote_url`, `remote_set_branches` |
54
- | `Git::Repository::Stashing` | `lib/git/repository/stashing.rb` | ✅ | `stash_save`, `stash_apply`, `stash_clear`, `stashes_all` |
55
- | `Git::Repository::Diffing` | `lib/git/repository/diffing.rb` | ✅ | `diff_path_status`, `diff_name_status`, `diff_full` |
56
- | `Git::Repository::Inspecting` | `lib/git/repository/inspecting.rb` | ✅ | `show`, `fsck` |
57
- | `Git::Repository::ObjectOperations` | `lib/git/repository/object_operations.rb` | ✅ | `rev_parse`, `tree_depth`, `ls_tree`, `grep`, `archive`, `tags`, `add_tag`, `delete_tag` |
59
+ | `Git::Repository::RemoteOperations` | `lib/git/repository/remote_operations.rb` | ✅ | `fetch`, `pull`, `push`, `remote_add` (alias: `add_remote`), `remote_remove` (alias: `remove_remote`), `remote_set_url` (alias: `set_remote_url`), `config_remote`, `remote`, `remotes`, `ls_remote`, `remote_set_branches` |
60
+ | `Git::Repository::Stashing` | `lib/git/repository/stashing.rb` | ✅ | `stash_list`, `stash_save`, `stash_apply`, `stash_clear`, `stashes_all` |
61
+ | `Git::Repository::Diffing` | `lib/git/repository/diffing.rb` | ✅ | `diff_full`, `diff_numstat`, `diff_stats`, `diff`, `diff_path_status` (alias: `diff_name_status`), `diff_files`, `diff_index` |
62
+ | `Git::Repository::Inspecting` | `lib/git/repository/inspecting.rb` | ✅ | `describe`, `show`, `fsck` |
58
63
  | `Git::Repository::Logging` | `lib/git/repository/logging.rb` | ✅ | `log`, `full_log_commits` |
59
- | `Git::Repository::StatusOperations` | `lib/git/repository/status_operations.rb` | ✅ | `ls_files`, `no_commits?` (renamed from `Git::Lib#empty?`), `untracked_files`, `status`; `Git::Base#ls_files` delegates to facade |
60
- | `Git::Repository::Configuring` | `lib/git/repository/configuring.rb` | ✅ | `config`; `Git::Base#config` delegates to facade |
64
+ | `Git::Repository::Maintenance` | `lib/git/repository/maintenance.rb` | ✅ | `repack`, `gc` |
65
+ | `Git::Repository::ObjectOperations` | `lib/git/repository/object_operations.rb` | ✅ | `cat_file_contents`, `cat_file_size`, `cat_file_type`, `cat_file_commit`, `cat_file_tag`, `rev_parse`, `tag_sha`, `full_tree`, `tree_depth`, `name_rev`, `ls_tree`, `grep`, `archive`, `gblob`, `gcommit`, `gtree`, `tag`, `object`, `tags`, `add_tag`, `delete_tag` |
66
+ | `Git::Repository::StatusOperations` | `lib/git/repository/status_operations.rb` | ✅ | `ls_files`, `no_commits?` / `empty?`, `untracked_files`, `status` |
67
+ | `Git::Repository::Configuring` | `lib/git/repository/configuring.rb` | ✅ | `config`, `config_get`, `config_list`, `config_set`, `global_config`, `global_config_get`, `global_config_list`, `global_config_set` |
61
68
  | `Git::Repository::WorktreeOperations` | `lib/git/repository/worktree_operations.rb` | ✅ | `worktrees_all`, `worktree_add`, `worktree_remove`, `worktree_prune`, `worktree`, `worktrees` |
62
69
 
63
70
  #### Facade module naming convention
64
71
 
65
- New topic modules follow a **two-tier** convention:
72
+ New topic modules follow a **three-tier** convention:
66
73
 
67
74
  - **Gerund** (verb-ing) when a single action word clearly names the whole module:
68
- `Staging`, `Committing`, `Branching`, `Merging`, `Logging`, `Diffing`, `Stashing`, `Configuring`.
75
+ `Staging`, `Committing`, `Branching`, `Merging`, `Logging`, `Diffing`, `Stashing`,
76
+ `Configuring`, `Inspecting`.
69
77
  - **Noun + `Operations`** when the module is a mixed bag of methods grouped by git
70
78
  concept rather than a single action: `RemoteOperations`, `ObjectOperations`,
71
79
  `StatusOperations`, `WorktreeOperations`.
80
+ - **Descriptive utility names** for cross-cutting helpers or housekeeping APIs that
81
+ are not domain-object names: `ContextHelpers`, `Maintenance`.
72
82
 
73
83
  Do **not** use plain nouns that clash with existing domain-object class names
74
84
  such as `Branch`, `Diff`, `Log`, `Object`, `Remote`, `Status`, `Worktree`, etc.
75
85
 
76
86
  ### Next Task
77
87
 
78
- #### Phase 3 Public Interface Completion
88
+ #### Phase 4 Step B (finalize test suite) is the next step
89
+
90
+ Phase 4 **Step A — Remove old code** is ✅ complete. The atomic removal landed in
91
+ [PR #1456](https://github.com/ruby-git/ruby-git/pull/1456) (commit `c1c53999`),
92
+ which deleted `Git::Base` and `Git::Lib`, removed the `base_object` / `from_base`
93
+ bridge from `Git::ExecutionContext::Repository`, and dropped the legacy `require`
94
+ lines from `lib/git.rb`. This also satisfies the long-standing **D2** redesign
95
+ item. The only remaining `Git::Base` / `Git::Lib` strings in `lib/` are YARD/comment
96
+ references to historical 4.x behavior, which the Step A done-criteria explicitly
97
+ allow.
98
+
99
+ The remaining redesign work is the rest of Phase 4:
100
+
101
+ | Step | Status | Summary |
102
+ | ---- | ------ | ------- |
103
+ | A — Remove old code | ✅ Complete | `Git::Base`/`Git::Lib` and the bridge deleted ([PR #1456](https://github.com/ruby-git/ruby-git/pull/1456)) |
104
+ | B — Finalize test suite | 🔲 Not Started | Port remaining Test::Unit coverage in `tests/units/` to RSpec, drop the `test-unit` dependency from `git.gemspec`, remove the `test`/`test-all` Rake tasks, and remove the `tests/` directory |
105
+ | C — Update documentation | 🚧 Partial | `UPGRADING.md` and broad `@api private` coverage exist; verify `yard stats` reports no missing public-API docs and that `README.md` reflects the new entry points |
106
+
107
+ The following earlier prerequisites are all ✅ complete:
108
+
109
+ | Step | Status |
110
+ | ---- | ------ |
111
+ | C1c-2: public-API parity audit and remediation sweep | ✅ |
112
+ | E: block-based helper/path-context methods migrated | ✅ |
113
+ | D2: remove the `base_object` / `from_base` bridge | ✅ |
114
+
115
+ Steps C1d-1, C1d-2, and C1d-3 are ✅ complete (see their detail sections below for full specs).
116
+
117
+ ---
118
+
119
+ #### Phase 3 Overview
79
120
 
80
121
  All 9 domain-object migrations are ✅ complete:
81
122
 
@@ -91,19 +132,10 @@ All 9 domain-object migrations are ✅ complete:
91
132
  | `Git::Branches` | [PR #1356](https://github.com/ruby-git/ruby-git/pull/1356), [PR #1357](https://github.com/ruby-git/ruby-git/pull/1357), [PR #1358](https://github.com/ruby-git/ruby-git/pull/1358), [PR #1359](https://github.com/ruby-git/ruby-git/pull/1359) |
92
133
  | `Git::Worktree` + `Git::Worktrees` | — |
93
134
 
94
- The remaining work splits into six workstreams:
95
-
96
- - Workstream A adds missing `Git::Repository` facade methods
97
- - B wires `Git::Base` factory methods to call the facade
98
- - C moves construction/global state and flips the top-level entry points
99
- - D removes compatibility bridges
100
- - E migrates helper/path-context methods
101
- - F removes the last `Git` module utility calls that still route through `Git::Lib`
102
-
103
- The required `Git::Commands::*` classes already exist (`Clone`, `LsRemote`,
104
- `ConfigOptionSyntax::*`, `Rm`, `Clean`, `Show`, `Fsck`, etc.). The remaining work is
105
- facade/adapter wiring, parser reuse, and public-API parity decisions — not new
106
- command-layer scaffolding. The full scope is defined in Workstreams A–F below.
135
+ The work was organized into six workstreams (A–F). All workstreams that are
136
+ prerequisites for C1d are now ✅ complete. F1 and F2 are both ✅ complete — F2
137
+ moved the remaining `Git` module utility methods off `Git::Lib`. Phase 4 Step A
138
+ is complete; Steps B and C remain (see [Next Task](#next-task)).
107
139
 
108
140
  **Sequencing** (see [Phase 3 dependency order](#phase-3-dependency-order) for the
109
141
  reasoning behind each edge):
@@ -121,8 +153,8 @@ graph LR
121
153
  C1a-2 --> C1c-2
122
154
  C1b --> C1c-2
123
155
  C1c-1 --> C1c-2
124
- C1c-2 --> C1d
125
- C1d --> D1 --> Phase4["Phase 4"]
156
+ C1c-2 --> C1d-1
157
+ C1d-1 --> C1d-2 --> C1d-3 --> D1 --> Phase4["Phase 4"]
126
158
  D2 --> Phase4
127
159
  F1 --> Phase4
128
160
  F2 --> Phase4
@@ -177,9 +209,10 @@ These are read-only repository inspection operations that don't fit an existing
177
209
 
178
210
  Files touched: `lib/git/repository/inspecting.rb` (new), `lib/git/repository.rb` (add `include Git::Repository::Inspecting`), `spec/unit/git/repository/inspecting_spec.rb` (new), `lib/git/base.rb`
179
211
 
180
- **Not covered by A1–A4:** lower-level public methods such as `describe`, `repack`,
181
- `gc`, `apply`, `apply_mail`, `read_tree`, and `cat_file` are handled by the C1c-2
182
- public-API parity audit before `Git.open` starts returning `Git::Repository`.
212
+ **Later covered by C1c-2:** lower-level public methods such as `describe`,
213
+ `repack`, `gc`, `apply`, `apply_mail`, `read_tree`, and `cat_file_*` were
214
+ subsequently migrated into `Inspecting`, `Maintenance`, `Staging`, and
215
+ `ObjectOperations` before `Git.open` started returning `Git::Repository`.
183
216
 
184
217
  ---
185
218
 
@@ -228,7 +261,7 @@ This workstream is intentionally split into two PRs because the path/accessor
228
261
  state work is independent of the clone/init work, and combining them would make a
229
262
  very large PR.
230
263
 
231
- **Step C1a-1 — Path state, accessors, and `.open`/`.bare` factories**
264
+ **Step C1a-1 — Path state, accessors, and `.open`/`.bare` factories**
232
265
 
233
266
  | `Git::Base` class method | Target |
234
267
  | --- | --- |
@@ -242,7 +275,7 @@ must also expose the path/accessor surface currently provided by `Git::Base`: `d
242
275
 
243
276
  Files touched: `lib/git/repository.rb`, `lib/git/base.rb`
244
277
 
245
- **Step C1a-2 — `.clone` and `.init` factories**
278
+ **Step C1a-2 — `.clone` and `.init` factories**
246
279
 
247
280
  | `Git::Base` / `Git` method | Target |
248
281
  | --- | --- |
@@ -259,7 +292,7 @@ Workstream F.
259
292
 
260
293
  Files touched: `lib/git/repository.rb`, `lib/git/base.rb`, `lib/git.rb`
261
294
 
262
- **Step C1b — Move global config singleton ownership off `Git::Base`**
295
+ **Step C1b — Move global config singleton ownership off `Git::Base`**
263
296
 
264
297
  `Git.configure` and `Git.config` both delegate to `Base.config`, which returns the
265
298
  `Git::Base`-owned `Git::Config` singleton. When `Git::Base` is deleted, these break.
@@ -286,7 +319,7 @@ documented as a v5 breaking change or already deprecated for removal. This audit
286
319
  the gate that prevents the entry-point flip from silently dropping public methods
287
320
  just because `Git::Base` still exists in the tree.
288
321
 
289
- **Step C1c-1 — Signature-compatibility guidance and process**
322
+ **Step C1c-1 — Signature-compatibility guidance and process**
290
323
 
291
324
  Update extraction and review skills to document the signature-compatibility
292
325
  classification policy (legacy-contract vs 5.x-native), parity-check requirements,
@@ -305,7 +338,7 @@ Files touched: `.github/skills/extract-facade-from-base-lib/SKILL.md`,
305
338
 
306
339
  Tracked as [Issue #1369](https://github.com/ruby-git/ruby-git/issues/1369).
307
340
 
308
- **Step C1c-2 — End-of-Phase-3 public-API parity audit and remediation sweep**
341
+ **Step C1c-2 — End-of-Phase-3 public-API parity audit and remediation sweep**
309
342
 
310
343
  Compare every public `Git::Base` method against `Git::Repository`; fix mismatches
311
344
  or explicitly record each as a documented v5 breaking change. No unclassified
@@ -317,7 +350,7 @@ Required audit buckets:
317
350
  | --- | --- |
318
351
  | Path/accessors | `dir`, `repo`, `index`, `repo_size` must exist on `Git::Repository` (C1a-1 owns this) |
319
352
  | Compatibility aliases/wrappers | `remove`, `revparse`, `diff_name_status`, `reset_hard`, `is_local_branch?`, `is_remote_branch?`, `is_branch?`, `checkout` must be migrated or intentionally removed with upgrade notes (`checkout` is called by `Git.export` on the `Git.clone` result) |
320
- | Low-level public methods | `describe`, `repack`, `gc`, `apply`, `apply_mail`, `read_tree`, `cat_file` must be migrated to topic modules or intentionally removed with upgrade notes |
353
+ | Low-level public methods | Resolved in the current tree: `describe` `Inspecting`; `repack`/`gc` `Maintenance`; `apply`/`apply_mail`/`read_tree` `Staging`; `cat_file_*` `ObjectOperations`. Any intentional removals still require upgrade notes. |
321
354
  | Factory/domain-object returns | Confirm B plus A2/A3 cover `branch`, `branches`, `remote`, `remotes`, `tag`, `tags`, object factories, and tag create/delete return shapes |
322
355
  | Keyword-arg facades | For `legacy-contract` methods, preserve the exact 4.x call shape (including rare `**opts` signatures); for `5.x-native` methods, use `opts = {}` style for consistency |
323
356
 
@@ -326,15 +359,38 @@ Files touched: `lib/git/repository/*.rb` (topic modules for migrated methods),
326
359
 
327
360
  Tracked as [Issue #1370](https://github.com/ruby-git/ruby-git/issues/1370).
328
361
 
329
- **Step C1d — Update `lib/git.rb` entry points to return `Git::Repository`**
362
+ ##### Step C1d-1Flip entry points
330
363
 
331
364
  With C1a, C1b, C1c, A, B, and E in place, update `Git.open`, `Git.clone`,
332
365
  `Git.init`, and `Git.bare` in `lib/git.rb` to call `Git::Repository.*` and return
333
366
  `Git::Repository` directly, bypassing `Git::Base` entirely.
334
367
 
368
+ **Prerequisite fix before landing C1d-1:** delete `spec/integration/git/lib/config_spec.rb`
369
+ after confirming behavior coverage exists in `spec/integration/git/repository/configuring_spec.rb`.
370
+ The `def lib = self` shim causes `subject(:lib) { repo.lib }` to return `Git::Repository` (self),
371
+ which triggers deprecation warnings from every deprecated alias call (config_set, config_get,
372
+ global_config_set, etc.) in that spec.
373
+
374
+ ##### Step C1d-2 — Eliminate internal `.lib` callers
375
+
376
+ Clean up all `.lib` callers from tests while the silent `def lib = self` shim is still in
377
+ place, then add `Git::Deprecation.behavior = :raise` to both test suites. See the phase table
378
+ in the [Next Task](#next-task) section for the full per-phase breakdown.
379
+
380
+ ##### Step C1d-3 — Remove dead fallbacks and add deprecation warning
381
+
382
+ - Add `diff_numstat` delegator to `Git::Base` (prerequisite: not currently defined there)
383
+ - Remove dead `respond_to?` + `.lib` fallback branches from `lib/git/diff.rb`,
384
+ `lib/git/diff_stats.rb`, `lib/git/diff_path_status.rb`, `lib/git/status.rb`
385
+ - Replace `def lib = self` in `lib/git/repository.rb` with a `Git::Deprecation.warn` body
386
+ (removal version: v6.0.0; returns `self`)
387
+ - Remove `command_capturing`, `command_streaming`, and private `env_overrides` from
388
+ `lib/git/repository.rb` — these were added only to feed the silent shim
389
+ - Add unit test asserting the deprecation warning and `self` return
390
+
335
391
  ---
336
392
 
337
- #### Workstream D — C2+C3: Remove compatibility bridges
393
+ #### Workstream D — C3: Remove compatibility fallbacks
338
394
 
339
395
  ⚠️ These are v5-only cleanup steps. They are not 4.x-compatible and must be kept out
340
396
  of 4.x release candidates unless an explicit breaking-change decision has already
@@ -379,20 +435,6 @@ After D1, domain objects should assume their provider is `Git::Repository` (or a
379
435
  compatible object that implements the repository facade methods directly), not an
380
436
  object with a `.lib` escape hatch.
381
437
 
382
- ##### Step D2 — Remove the `base_object` bridge
383
-
384
- ⚠️ Do **not** ship D2 as a standalone PR while `Git::Base` remains functional.
385
- `Git::Base#facade_repository` currently depends on
386
- `Git::ExecutionContext::Repository.from_base(self)`, so deleting the bridge before
387
- deleting or retiring `Git::Base` would leave the tree unreleasable.
388
-
389
- D2 belongs in the Phase 4 old-code deletion PR (or in the same v5 cleanup PR that
390
- removes `Git::Base` as a usable public entry point):
391
-
392
- - Delete `attr_reader :base_object`
393
- - Remove `base_object:` from `#initialize` and body
394
- - Remove or convert `from_base` factory
395
-
396
438
  ---
397
439
 
398
440
  #### Workstream E — Migrate or deprecate instance helper methods
@@ -405,7 +447,7 @@ helpers would drop existing public `Git::Base` behavior.
405
447
  `Git::Repository`. They must either be migrated before `Git::Base` can be deleted,
406
448
  or explicitly deprecated with removal in v6.0. The recommended path is migration.
407
449
 
408
- **Step E — Migrate block-based helper/path-context methods to `Git::Repository`**
450
+ **Step E — Migrate block-based helper/path-context methods to `Git::Repository`**
409
451
 
410
452
  | `Git::Base` method | Proposed destination | Notes |
411
453
  | --- | --- | --- |
@@ -419,8 +461,8 @@ or explicitly deprecated with removal in v6.0. The recommended path is migration
419
461
  removed at the same time, since `with_index`/`with_working` depend on the same
420
462
  invalidation logic.
421
463
 
422
- Files touched: `lib/git/repository.rb` (or a new `Git::Repository::ContextHelpers`
423
- module), `lib/git/base.rb`, `spec/unit/git/repository/` (new or extended spec)
464
+ Files touched: `lib/git/repository/context_helpers.rb`, `lib/git/base.rb`,
465
+ `spec/unit/git/repository/` (new or extended spec)
424
466
 
425
467
  ---
426
468
 
@@ -434,7 +476,7 @@ Three `Git`-module-level methods bypass `Git::Repository` entirely and call
434
476
  non-`Git::Lib` adapter path using `Git::ExecutionContext::Global` plus existing
435
477
  parsing logic.
436
478
 
437
- **Step F1 — Move `Git.ls_remote` and `Git.default_branch` off `Git::Lib`**
479
+ **Step F1 — Move `Git.ls_remote` and `Git.default_branch` off `Git::Lib`**
438
480
 
439
481
  | `Git` module method | Current path | Required work |
440
482
  | --- | --- | --- |
@@ -449,7 +491,7 @@ use the same command class.
449
491
  Files touched: `lib/git.rb`, `lib/git/base.rb`, and parser/helper code extracted
450
492
  from `Git::Lib` as needed
451
493
 
452
- **Step F2 — Move `Git.global_config`, `#config`, and `#global_config` off `Git::Lib`**
494
+ **Step F2 — Move `Git.global_config`, `#config`, and `#global_config` off `Git::Lib`**
453
495
 
454
496
  | `Git` module method | Current path | Required work |
455
497
  | --- | --- | --- |
@@ -494,19 +536,15 @@ classified as a v5-only PR with upgrade-note coverage before it lands.
494
536
  | A4 | ✅ | Add `Inspecting#show` and `#fsck` | 4.x-compatible | `Git::Base#show` and `#fsck` remain behavior-compatible and delegate internally. |
495
537
  | B | ✅ | Redirect `Git::Base` domain-object factories | 4.x-compatible | Method signatures and return types stay the same; only the internal provider changes to `Git::Repository`. Split into object/tag factories and branch/remote factories if the PR grows. |
496
538
  | C1a-1 | ✅ | Add `Git::Repository.open`/`.bare`, path state, and `dir`/`repo`/`index`/`repo_size` | 4.x-compatible additive | `Git.open`/`.bare` still return `Git::Base`; new repository factories are additive until C1d. |
497
- | C1a-2 | | Add `Git::Repository.clone`/`.init` | 4.x-compatible additive | `Git.clone`/`.init` still return `Git::Base`; clone/init behavior is duplicated behind new factories without changing public entry points. |
498
- | C1b | | Move global config ownership | 4.x-compatible | `Git.config`, `Git.configure`, and `Git::Base.config` keep working; `Git::Base.config` remains as a delegator. |
499
- | E | | Add repository helper/path-context methods | 4.x-compatible additive | `Git::Base` helpers keep working; `Git::Repository` gains equivalent behavior before any top-level return-type change. Split index helpers and working-directory helpers if needed. |
500
- | F1 | | Move `Git.ls_remote` and `Git.default_branch` off `Git::Lib` | 4.x-compatible | Return formats and error behavior match current 4.x-compatible behavior. |
501
- | F2 | | Move `Git.global_config`, module `#config`, and module `#global_config` off `Git::Lib` | 4.x-compatible | Config methods keep the same return formats and write behavior. |
502
- | C1c-1 | | Guidance/process: signature-compatibility policy for extraction and review ([#1369](https://github.com/ruby-git/ruby-git/issues/1369)) | 4.x-compatible / docs-only | Guidance and review checklists define legacy-contract vs 5.x-native signatures, including test expectations. |
503
- | C1c-2 | | End-of-Phase-3 public-API parity audit and remediation sweep ([#1370](https://github.com/ruby-git/ruby-git/issues/1370)) | 4.x-compatible | All four parity audit buckets resolved (fix or documented removal) before C1d; no unclassified compatibility gap remains. |
504
- | C1d | | Flip `Git.open`/`.clone`/`.init`/`.bare` to return `Git::Repository` | v5 boundary | Explicit breaking change because class identity changes from `Git::Base` to `Git::Repository`; method-level parity must be complete first. |
505
- | D1 | | Remove domain-object `Git::Base` guards and `@base.lib` fallbacks | v5 cleanup | Explicitly drops direct `Git::Base` provider support in domain-object constructors; normal factory-created objects remain supported. |
506
- | D2 / Phase 4 | ⬜ | Remove `base_object`/`from_base` with `Git::Base` deletion or retirement | v5 cleanup | Must land with the old-code deletion path; not releasable as a standalone PR while `Git::Base#facade_repository` depends on it. |
507
-
508
- ---
509
-
539
+ | C1a-2 | | Add `Git::Repository.clone`/`.init` | 4.x-compatible additive | `Git.clone`/`.init` still return `Git::Base`; clone/init behavior is duplicated behind new factories without changing public entry points. |
540
+ | C1b | [#1385](https://github.com/ruby-git/ruby-git/pull/1385) | Move global config ownership | 4.x-compatible | `Git.config`, `Git.configure`, and `Git::Base.config` keep working; `Git::Base.config` remains as a delegator. |
541
+ | E | | Add repository helper/path-context methods | 4.x-compatible additive | `Git::Base` helpers keep working; `Git::Repository` gains equivalent behavior before any top-level return-type change. Split index helpers and working-directory helpers if needed. |
542
+ | F1 | | Move `Git.ls_remote` and `Git.default_branch` off `Git::Lib` | 4.x-compatible | Return formats and error behavior match current 4.x-compatible behavior. |
543
+ | F2 | | Move `Git.global_config`, module `#config`, and module `#global_config` off `Git::Lib` | 4.x-compatible | Config methods keep the same return formats and write behavior. |
544
+ | C1c-1 | | Guidance/process: signature-compatibility policy for extraction and review ([#1369](https://github.com/ruby-git/ruby-git/issues/1369)) | 4.x-compatible / docs-only | Guidance and review checklists define legacy-contract vs 5.x-native signatures, including test expectations. |
545
+ | C1c-2 | | End-of-Phase-3 public-API parity audit and remediation sweep ([#1370](https://github.com/ruby-git/ruby-git/issues/1370)) | 4.x-compatible | All four parity audit buckets resolved (fix or documented removal) before C1d; no unclassified compatibility gap remains. |
546
+ | C1d | | Flip `Git.open`/`.clone`/`.init`/`.bare` to return `Git::Repository` | v5 boundary | Explicit breaking change because class identity changes from `Git::Base` to `Git::Repository`; method-level parity must be complete first. Split into C1d-1 (entry point flip), C1d-2 (eliminate internal .lib callers + add deprecation enforcement to both test suites), and C1d-3 (remove dead fallbacks + replace silent lib shim with real deprecation warning). |
547
+ | D1 | | Remove domain-object `Git::Base` guards and `@base.lib` fallbacks | v5 cleanup | Explicitly drops direct `Git::Base` provider support in domain-object constructors; normal factory-created objects remain supported. |
510
548
  #### Phase 3 completion criteria
511
549
 
512
550
  Use this table to decide whether a checklist item can be marked complete. A step is
@@ -526,7 +564,6 @@ done only when its code, focused specs, and delegation/cleanup checks are all tr
526
564
  | C1c-2: public-API parity audit and remediation | End-of-Phase-3 sweep complete (Issue #1370): a public-method inventory compares `Git::Base` and `Git::Repository`; every surviving public method has a repository implementation and focused coverage; every intentional removal has an upgrade-note/deprecation decision; no unclassified compatibility gap remains. |
527
565
  | C1d: entry-point flip | `Git.open`, `Git.clone`, `Git.init`, and `Git.bare` return `Git::Repository`; common existing workflows still pass through those entry points; YARD return docs are updated; no top-level factory method calls `Git::Base.*`; full suite passes. |
528
566
  | D1: domain-object fallback removal | No `is_a?(Git::Base)` guards remain; no `@base.lib` fallback remains in domain objects; direct `Git::Base` provider support is documented as a v5-only removal; full suite passes. |
529
- | D2 / Phase 4: `base_object` bridge removal | `Git::ExecutionContext::Repository` no longer accepts or exposes `base_object`; `from_base` is removed or converted to a non-`Git::Base` path; this lands only with the PR that deletes or retires `Git::Base`, so no releasable state contains a broken `Git::Base#facade_repository`. |
530
567
  | E: instance helper methods | `Git::Repository` implements or explicitly deprecates `chdir`, `with_index`, `with_temp_index`, `with_working`, `with_temp_working`, `set_index`, and `set_working`; context rebuilding after index/worktree changes is covered by specs; helpers yield the same values and restore state after block exit/errors. |
531
568
  | F: `Git` module utilities off `Git::Lib` | `Git.default_branch`, `Git.global_config`, `Git.ls_remote`, module instance `#config`, and module instance `#global_config` no longer call `Git::Lib.new`; `Git::Base.repository_default_branch` migrated to use `Git::Commands::LsRemote` directly; existing `LsRemote` and `ConfigOptionSyntax` commands provide the behavior; parser/helper code needed from `Git::Lib` has moved; `grep -n 'Lib.new' lib/git.rb` returns no matches. |
532
569
 
@@ -542,15 +579,14 @@ done only when its code, focused specs, and delegation/cleanup checks are all tr
542
579
  | A4: new `Inspecting` — `show`, `fsck` | ✅ |
543
580
  | B (C0): `Git::Base` factory delegation wiring | ✅ |
544
581
  | C1a-1: `Git::Repository.open`/`.bare`, path state (`dir`, `repo`, `index`, `repo_size`) | ✅ |
545
- | C1a-2: `Git::Repository.clone`/`.init` (no `Git::Lib` dependency) | |
546
- | C1b: Global config ownership (`Base.config` → `Git::Config`) | |
547
- | C1c-1: Guidance/process updates for signature compatibility (#1369) | |
548
- | C1c-2: End-of-Phase-3 public-API parity audit and remediation (#1370) | |
549
- | C1d: Entry-point flip (`Git.open` etc. → `Git::Repository`) | |
550
- | D1 (C3): Remove `is_a?(Git::Base)` guards + `@base.lib` fallbacks | (v5 cleanup) |
551
- | D2 (C2 / Phase 4): Remove `base_object` bridge with `Git::Base` deletion | (v5 cleanup) |
552
- | E: Instance helpers (`#chdir`, `#with_index`, `#with_temp_index`, `#with_working`, `#with_temp_working`) | |
553
- | F: `Git` module utilities (`default_branch`, `global_config`, `ls_remote`) off `Git::Lib` | ⬜ (Phase 4 prereq) |
582
+ | C1a-2: `Git::Repository.clone`/`.init` (no `Git::Lib` dependency) | |
583
+ | C1b: Global config ownership (`Base.config` → `Git::Config`) | |
584
+ | C1c-1: Guidance/process updates for signature compatibility (#1369) | |
585
+ | C1c-2: End-of-Phase-3 public-API parity audit and remediation (#1370) | |
586
+ | C1d: Entry-point flip (`Git.open` etc. → `Git::Repository`) | |
587
+ | D1 (C3): Remove `is_a?(Git::Base)` guards + `@base.lib` fallbacks | |
588
+ | E: Instance helpers (`#chdir`, `#with_index`, `#with_temp_index`, `#with_working`, `#with_temp_working`) | |
589
+ | F: `Git` module utilities (`default_branch`, `global_config`, `ls_remote`) off `Git::Lib` | |
554
590
 
555
591
  #### Quality gates (per step)
556
592
 
@@ -580,7 +616,7 @@ logic. The gem will be fully functional after this phase.*
580
616
  1. **Create New Directory Structure**
581
617
 
582
618
  - `lib/git/commands/` ✅
583
- - `lib/git/repository/` ✅ — populated with 12 facade modules in Phase 3 (see [Facade Modules Completed](#facade-modules-completed))
619
+ - `lib/git/repository/` ✅ — populated with 15 included modules in Phase 3 (see [Facade Modules Completed](#facade-modules-completed))
584
620
 
585
621
  2. **Eliminate Custom Path Classes**
586
622
 
@@ -602,14 +638,14 @@ logic. The gem will be fully functional after this phase.*
602
638
  - `Git::ExecutionContext::Global` subclass in `lib/git/execution_context/global.rb` ✅
603
639
 
604
640
  - `Git::Repository` in `lib/git/repository.rb` ✅
605
- - Now includes 12 facade modules (see [Facade Modules Completed](#facade-modules-completed))
641
+ - Now includes 15 modules via `include` (see [Facade Modules Completed](#facade-modules-completed))
606
642
 
607
643
  - `Git::Commands::Arguments` DSL in `lib/git/commands/arguments.rb` ✅
608
644
  - Provides declarative argument definition for command classes
609
645
 
610
646
  4. **Set Up RSpec Environment**
611
647
 
612
- RSpec configured and working alongside TestUnit. Specs live in `spec/` and can be
648
+ RSpec configured and working alongside Test::Unit. Specs live in `spec/` and can be
613
649
  run with `bundle exec rspec`. ✅
614
650
 
615
651
  ## Phase 2: The Strangler Fig Pattern - Migrating Commands
@@ -1254,7 +1290,7 @@ future work:
1254
1290
  instantiate and call the new `Git::Commands::Add` object, passing `self` as the
1255
1291
  context.
1256
1292
 
1257
- - **Verify**: Run the full test suite (both TestUnit and RSpec). The existing tests
1293
+ - **Verify**: Run the full test suite (both Test::Unit and RSpec). The existing tests
1258
1294
  for `g.add` should still pass, but they will now be executing the new, refactored
1259
1295
  code.
1260
1296
 
@@ -1483,7 +1519,9 @@ order: Basic Snapshotting → Branching & Merging → etc.
1483
1519
  ***Goal**: Switch the public-facing classes to use the new architecture directly,
1484
1520
  breaking the final ties to the old implementation.*
1485
1521
 
1486
- > **Status**: Task 1 below is complete; Task 2 is in progress see the [Next Task](#next-task) section above for the current plan.
1522
+ > **Status**: Phase 3 is complete. Tasks 1 and 2 are both complete; the remaining
1523
+ > redesign work is the Phase 4 cleanup described in the [Next Task](#next-task)
1524
+ > section above.
1487
1525
 
1488
1526
  1. **Add `binary_path:` to `Git::ExecutionContext`** ✅
1489
1527
 
@@ -1493,43 +1531,63 @@ breaking the final ties to the old implementation.*
1493
1531
  stopgap in `Git.run_git_version` was replaced with
1494
1532
  `Git::Commands::Version.new(Git::ExecutionContext::Global.new(...)).call`.
1495
1533
 
1496
- 2. **Implement the Facade**
1534
+ 2. **Implement the Facade**
1497
1535
 
1498
- `Git::Repository` now includes 12 facade modules (see
1499
- [Facade Modules Completed](#facade-modules-completed)). `Git::Base` wraps the
1500
- corresponding methods via `facade_repository`. The domain-object migration
1501
- iterations (iters 1–9) add further facade methods, but full `Git::Base`
1502
- coverage requires additional facade PRs for methods not touched by those
1503
- iterations (e.g. `clean`, `rm`, `tags`/`add_tag`, `fsck`, `show`, `remotes`,
1504
- remote URL/branch helpers) before the cleanup PR can run.
1536
+ `Git::Repository` now includes 15 modules via `include` in
1537
+ `lib/git/repository.rb` (see [Facade Modules Completed](#facade-modules-completed)).
1538
+ `Git::Base` wraps the corresponding methods via `facade_repository`, and the
1539
+ end-of-Phase-3 parity sweep landed the remaining low-level facade coverage
1540
+ needed before the entry-point flip including `describe`, `show`, `fsck`,
1541
+ `apply`, `apply_mail`, `read_tree`, `cat_file_*`, `repack`, `gc`, helper/path
1542
+ context methods, and the remaining remote/tag convenience APIs.
1505
1543
 
1506
1544
  ## Phase 4: Final Cleanup and Release Preparation
1507
1545
 
1508
1546
  ***Goal**: Remove all old code, finalize the test suite, and prepare for the v5.0.0
1509
1547
  release.*
1510
1548
 
1511
- 1. **Remove Old Code**:
1549
+ ### Phase 4 step graph
1550
+
1551
+ ```mermaid
1552
+ graph LR
1553
+ A --> B
1554
+ B --> C
1555
+ ```
1556
+
1557
+ #### Step A — Remove old code
1558
+
1559
+ **Status: ✅ Complete.** Completed in [PR #1456](https://github.com/ruby-git/ruby-git/pull/1456)
1560
+ (commit `c1c53999`).
1512
1561
 
1513
- - Delete the `Git::Lib` class entirely.
1562
+ - Delete `attr_reader :base_object`, remove `base_object:` from `#initialize`, and remove or convert `from_base`.
1563
+ - Delete the `Git::Lib` class entirely.
1564
+ - Delete the `Git::Base` class file.
1565
+ - Remove any other dead code that was part of the old implementation.
1514
1566
 
1515
- - Delete the `Git::Base` class file.
1567
+ **Done when**: `lib/git/lib.rb` and `lib/git/base.rb` are deleted; `Git::ExecutionContext::Repository` no longer accepts or exposes `base_object`; no runtime references to `Git::Lib` or `Git::Base` remain in `lib/` (YARD/comment references to historical 4.x behavior are allowed). ✅ Met — the only surviving `Git::Lib`/`Git::Base` strings in `lib/` are such doc/comment references.
1516
1568
 
1517
- - Remove any other dead code that was part of the old implementation.
1569
+ **Planning tip**: Before generating a deletion plan, audit all remaining callers of `Git::Lib`, `Git::Base`, and `from_base` across `lib/` and `spec/`. The bridge removal and `Git::Base` deletion must land atomically in the same PR — plan for a single large deletion commit rather than incremental removals.
1518
1570
 
1519
- 2. **Finalize Test Suite**:
1571
+ #### Step B — Finalize test suite
1520
1572
 
1521
- - Convert any remaining, relevant TestUnit tests to RSpec.
1573
+ **Status: 🔲 Not Started.**
1522
1574
 
1523
- - Remove the `test-unit` dependency from the `Gemfile`.
1575
+ - Convert any remaining, relevant Test::Unit tests to RSpec.
1576
+ - Remove the `test-unit` dependency from `git.gemspec`.
1577
+ - Ensure the RSpec suite has comprehensive coverage for the new architecture.
1524
1578
 
1525
- - Ensure the RSpec suite has comprehensive coverage for the new architecture.
1579
+ **Done when**: The `tests/` directory is empty or removed; `test-unit` is no longer
1580
+ in `git.gemspec`; RSpec is the sole test framework.
1526
1581
 
1527
- 3. **Update Documentation**:
1582
+ #### Step C — Update documentation
1528
1583
 
1529
- - Thoroughly document the new public API (`Git`, `Git::Repository`, etc.).
1584
+ **Status: 🚧 Partial.**
1530
1585
 
1531
- - Mark all internal classes (`ExecutionContext`, `Commands`, `*Path`) with `@api
1532
- private` in the YARD documentation.
1586
+ - Thoroughly document the new public API (`Git`, `Git::Repository`, etc.).
1587
+ - Mark all internal classes (`ExecutionContext`, `Commands`, `*Path`) with `@api private` in the YARD documentation.
1588
+ - Update the `README.md` and create a `UPGRADING.md` guide explaining the breaking changes for v5.0.0.
1533
1589
 
1534
- - Update the `README.md` and create a `UPGRADING.md` guide explaining the breaking
1535
- changes for v5.0.0.
1590
+ **Done when**: `yard stats` reports no missing docs on public API; `UPGRADING.md`
1591
+ covers all breaking changes; `README.md` reflects the new entry points. `UPGRADING.md`
1592
+ exists and `@api private` markers are applied broadly; remaining work is to confirm
1593
+ full public-API doc coverage and that `README.md` reflects the new entry points.