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
@@ -0,0 +1,366 @@
1
+ # Phase 4 / Step A — Remove Old Code: Execution Plan
2
+
3
+ > **✅ Status: Complete.** All PRs in this plan (1a–1d, 2a–2c, 3a–3d, and the
4
+ > atomic removal PR 4) have merged. The final removal landed in
5
+ > [PR #1456](https://github.com/ruby-git/ruby-git/pull/1456) (commit `c1c53999`),
6
+ > deleting `Git::Base`, `Git::Lib`, and the `base_object` / `from_base` bridge.
7
+ > This document is retained as a historical record of how Step A was executed.
8
+
9
+ ## Goal
10
+
11
+ Delete `Git::Base`, `Git::Lib`, and the `from_base`/`base_object` bridge in a
12
+ single atomic breaking PR. The preceding PRs are strictly preparatory and
13
+ backward-compatible — each is releasable independently unless a dependency is
14
+ noted.
15
+
16
+ ---
17
+
18
+ ## Done-When Criteria
19
+
20
+ - `lib/git/lib.rb` and `lib/git/base.rb` are deleted.
21
+ - `Git::ExecutionContext::Repository` has no `base_object:` param, no
22
+ `attr_reader :base_object`, no `.from_base` factory, and no propagation of
23
+ `@base_object` in `dup_with`.
24
+ - `grep -rEn 'Git::Lib|Git::Base' lib/` returns only YARD history comments,
25
+ no runtime references.
26
+ - Entry-point behavior (`Git.open`, `.clone`, `.init`, `.bare`,
27
+ `.default_branch`, `.git_version`) still works entirely through
28
+ `Git::Repository` / command / parser paths.
29
+ - Full CI pipeline (RSpec + Test::Unit + linters) is green after all removals.
30
+
31
+ ---
32
+
33
+ ## Sequencing
34
+
35
+ PRs with no incoming edges may be merged in any order or in parallel. PR 4
36
+ cannot begin until every other PR has merged.
37
+
38
+ ```mermaid
39
+ graph TD
40
+ PR1a["PR 1a"]
41
+ PR1b["PR 1b"]
42
+ PR1c["PR 1c"]
43
+ PR1d["PR 1d"]
44
+ PR2a["PR 2a"]
45
+ PR2b["PR 2b"]
46
+ PR2c["PR 2c"]
47
+ PR3a["PR 3a"]
48
+ PR3b["PR 3b"]
49
+ PR3c["PR 3c"]
50
+ PR3d["PR 3d"]
51
+ PR4["PR 4"]
52
+
53
+ PR1a --> PR4
54
+ PR1b --> PR4
55
+ PR1c --> PR4
56
+ PR1d --> PR4
57
+ PR2a --> PR4
58
+ PR2b --> PR4
59
+ PR2c --> PR4
60
+ PR3a --> PR4
61
+ PR3b --> PR4
62
+ PR3c --> PR4
63
+ PR3d --> PR4
64
+ ```
65
+
66
+ ---
67
+
68
+ ## PR 1a — Rewire `Git.default_branch`
69
+
70
+ **Releasable independently. No breaking changes.**
71
+
72
+ In `lib/git.rb`, replace `Git.default_branch`'s delegation to
73
+ `Base.repository_default_branch` with the direct
74
+ `Git::Commands::LsRemote` + `Git::Parsers::LsRemote.parse_default_branch`
75
+ flow.
76
+
77
+ **Co-merge the spec that asserts the old delegation** — this rewire breaks an
78
+ existing expectation, so its update must ship in this PR to keep CI green:
79
+
80
+ - `spec/unit/git/git_remote_utilities_spec.rb` — the `.default_branch` example
81
+ asserts `expect(Git::Base).to receive(:repository_default_branch)`. Replace
82
+ it with a test against the direct `LsRemote` + parser path.
83
+
84
+ (The `.repository_default_branch` example in `spec/unit/git/base_spec.rb` tests
85
+ `Git::Base` directly, still passes, and is deleted by PR 3c — leave it alone
86
+ here.)
87
+
88
+ **Gate:** `Git.default_branch` works; full CI green.
89
+
90
+ ---
91
+
92
+ ## PR 1b — Rewire `Git.git_version`
93
+
94
+ **Releasable independently. No breaking changes.**
95
+
96
+ Move the git-version cache out of `Git::Lib` to a module-level cache on `Git`
97
+ (in `lib/git.rb`) so that `Git.git_version` no longer routes through
98
+ `Git::Lib`.
99
+
100
+ - **Relocate the cache state.** Move the cache initialization currently in
101
+ `Git::Lib` (`@git_version_cache = {}` and `@git_version_cache_mutex =
102
+ Mutex.new`) to module-level instance variables on `Git` in `lib/git.rb`.
103
+ Keep the mutex — it guards parallelism on JRuby/TruffleRuby.
104
+ - **Relocate the cache API.** Add `Git.cached_git_version(binary_path,
105
+ &block)` (memoize per binary path) and `Git.clear_git_version_cache`
106
+ (used by tests) as class methods on `Git`. Mark both `@api private`.
107
+ - **Rewire the caller.** In `lib/git.rb`, change `Git.git_version` to call
108
+ `Git.cached_git_version` instead of `Git::Lib.cached_git_version`
109
+ ([lib/git.rb#L512](../lib/git.rb#L512)).
110
+ - The instance method `Git::Lib#git_version` and the class methods on
111
+ `Git::Lib` are deleted wholesale in PR 4, so they need no rewiring here; do
112
+ not leave a second live cache behind.
113
+
114
+ **Co-merge the specs that assert the old cache owner** — this rewire breaks
115
+ existing expectations, so their updates must ship in this PR to keep CI green:
116
+
117
+ - `spec/unit/git/git_configure_spec.rb` — replace the
118
+ `Git::Lib.clear_git_version_cache` setup and the
119
+ `expect(Git::Lib).to have_received(:cached_git_version)` assertion with
120
+ `Git.clear_git_version_cache` / `Git.cached_git_version`.
121
+ - `spec/unit/git/git_version_spec.rb` — replace the
122
+ `before { Git::Lib.clear_git_version_cache }` setup with
123
+ `Git.clear_git_version_cache`; otherwise it clears the now-unused cache and
124
+ the new `Git` cache leaks across examples (flaky/failing per-binary-path
125
+ tests).
126
+
127
+ **Gate:** `Git.git_version` works and caches per binary path; full CI green.
128
+
129
+ ---
130
+
131
+ ## PR 1c — Remove `require 'git/base'` from Domain Object Files
132
+
133
+ **Releasable independently. No breaking changes.**
134
+
135
+ Remove `require 'git/base'` from each of these files:
136
+ `branch.rb`, `branches.rb`, `remote.rb`, `object.rb`, `log.rb`,
137
+ `stash.rb`, `stashes.rb`, `worktree.rb`, `worktrees.rb`.
138
+
139
+ This is safe because `lib/git.rb` still requires `git/base` and `git/lib`
140
+ directly, so both constants remain loaded. The only effect is that `base.rb`
141
+ is no longer force-loaded early (out of order) by the domain-object files;
142
+ load order then depends entirely on `lib/git.rb`, which the CI gate verifies.
143
+ Doing this early is beneficial — it shrinks PR 4 and exercises the
144
+ `lib/git.rb` load order under CI ahead of the breaking change.
145
+
146
+ > The removal of the `require 'git/lib'` / `require 'git/base'` lines from
147
+ > `lib/git.rb` itself is **not** done here — that is the breaking change and is
148
+ > folded into [PR 4](#pr-4--atomic-removal).
149
+
150
+ **Gate:** full CI green; no `require 'git/base'` in domain object files.
151
+
152
+ ---
153
+
154
+ ## PR 1d — Update `{Git::Base}`/`{Git::Lib}` YARD Cross-References in Domain Object and Command Files
155
+
156
+ **Releasable independently. Documentation only.**
157
+
158
+ `.yardopts` sets `--fail-on-warning`, so any `{Git::Base#…}` or `{Git::Lib#…}` YARD
159
+ cross-reference link that survives into PR 4 becomes a CI failure the moment
160
+ `Git::Base` and `Git::Lib` are deleted. PRs 2a–2c cover the command layer,
161
+ repository layer, and diff/status domain types, but five additional files have
162
+ unresolved cross-references that are not covered by those PRs.
163
+
164
+ ### `lib/git/branch.rb`
165
+
166
+ Replace two class-doc and one method-param cross-references:
167
+
168
+ - Class docstring (lines 9–10): `{Git::Base#branch}` and `{Git::Base#branches}`
169
+ → `{Git::Repository#branch}` and `{Git::Repository#branches}`.
170
+ - `attr_accessor :full` docstring (line 26): `{Git::Base#branches}`
171
+ → `{Git::Repository#branches}`.
172
+ - `archive` method `@param opts` (line 149): `{Git::Base#archive}`
173
+ → `{Git::Repository#archive}`.
174
+
175
+ ### `lib/git/remote.rb`
176
+
177
+ Replace two cross-references:
178
+
179
+ - Class docstring (line 10): `{Git::Base#remote}` → `{Git::Repository#remote}`.
180
+ - `fetch` method `@param opts` (line 63): `{Git::Base#fetch}`
181
+ → `{Git::Repository#fetch}`.
182
+
183
+ ### `lib/git/object.rb`
184
+
185
+ - `archive` method `@param opts` (line 105): `{Git::Lib#archive}`
186
+ → `{Git::Repository#archive}`.
187
+
188
+ ### `lib/git/commands/base.rb`
189
+
190
+ - `#initialize` `@param execution_context` (lines 176–177): change type from
191
+ `[Git::ExecutionContext, Git::Lib]` to `[Git::ExecutionContext]`; replace
192
+ `{Git::Lib#command_capturing}` / `{Git::Lib#command_streaming}` with
193
+ `{Git::ExecutionContext#command_capturing}` /
194
+ `{Git::ExecutionContext#command_streaming}`.
195
+ - `with_stdin_pipe` inline doc (line 354): `{Git::Lib#command_capturing}`
196
+ → `{Git::ExecutionContext#command_capturing}`.
197
+
198
+ ### `lib/git/version.rb`
199
+
200
+ - `to_a` docstring (line 103): demote `{Git::Lib#current_command_version}` from a
201
+ YARD cross-reference link to a code literal
202
+ (`Git::Lib#current_command_version`), since `Git::Lib` will not exist
203
+ after PR 4.
204
+
205
+ **Gate:** linters and YARD doc build pass (`bundle exec rake rubocop yard build`);
206
+ no behavior changes.
207
+
208
+ ---
209
+
210
+ ## PR 2a — Update Command-Layer YARD
211
+
212
+ **Releasable independently. Documentation only.**
213
+
214
+ - `lib/git/commands.rb` — update architecture diagram to reference
215
+ `Git::Repository` instead of `Git::Base`/`Git::Lib`.
216
+ - `lib/git/command_line.rb` — update factory helper references.
217
+ - `lib/git/command_line/capturing.rb`, `streaming.rb` — update
218
+ `@see Git::Lib#…` tags.
219
+
220
+ **Gate:** linters and YARD doc build pass; no behavior changes.
221
+
222
+ ---
223
+
224
+ ## PR 2b — Update Repository-Layer YARD
225
+
226
+ **Releasable independently. Documentation only.**
227
+
228
+ - `lib/git/repository/path_resolver.rb` — update `Git::Base.config.binary_path`
229
+ reference.
230
+ - `lib/git/repository/factories.rb` — update or remove deprecation message
231
+ strings mentioning `Git::Lib#clone`.
232
+
233
+ **Gate:** linters and YARD doc build pass; no behavior changes.
234
+
235
+ ---
236
+
237
+ ## PR 2c — Update Domain Object `@param base` YARD Types
238
+
239
+ **Releasable independently. Documentation only.**
240
+
241
+ In `diff.rb`, `status.rb`, `diff_path_status.rb`, update `@param base`
242
+ YARD type from `[Git::Base, Git::Repository]` → `[Git::Repository]`.
243
+
244
+ **Gate:** linters and YARD doc build pass; no behavior changes.
245
+
246
+ ---
247
+
248
+ ## PR 3a — Delete Confirmed-Removable Test::Unit Files
249
+
250
+ **Releasable independently. No production behavior changes.**
251
+
252
+ Delete these files, which test removed API only and have no surviving
253
+ behavioral value:
254
+
255
+ | File | Reason |
256
+ | --- | --- |
257
+ | `tests/units/test_base.rb` | Tests `Git::Base` directly |
258
+ | `tests/units/test_lib.rb` | Tests `Git::Lib` directly |
259
+ | `tests/units/test_lib_repository_default_branch.rb` | Tests `Git::Lib` default-branch path |
260
+ | `tests/units/test_lib_meets_required_version.rb` | Tests `Git::Lib` version deprecation shim |
261
+ | `tests/units/test_git_base_root_of_worktree.rb` | Tests `Git::Base.root_of_worktree` |
262
+
263
+ **Gate:** full CI green.
264
+
265
+ ---
266
+
267
+ ## PR 3b — Patch Incidental Test::Unit References
268
+
269
+ **Releasable independently. No production behavior changes.**
270
+
271
+ Audit all remaining `tests/units/` files for incidental `Git::Base`/`Git::Lib`
272
+ references and patch them to use `Git::Repository` / `Git.open` entry points.
273
+ Port any still-valuable behavioral coverage to RSpec before removing a file.
274
+
275
+ **Gate:** full CI green; no `tests/units/` file references `Git::Base`
276
+ or `Git::Lib` at runtime.
277
+
278
+ ---
279
+
280
+ ## PR 3c — Delete RSpec Specs Testing Only Removed API
281
+
282
+ **Releasable independently. No production behavior changes.**
283
+
284
+ | File | Reason |
285
+ | --- | --- |
286
+ | `spec/unit/git/base_spec.rb` | Tests `Git::Base` |
287
+ | `spec/unit/git/lib_command_spec.rb` | Tests `Git::Lib` |
288
+ | `spec/unit/git/lib_version_deprecations_spec.rb` | Tests `Git::Lib` version deprecation shims |
289
+ | `spec/unit/git/lib/git_version_spec.rb` | Tests `Git::Lib` version cache |
290
+ | `spec/integration/git/base/` (all files) | Tests `Git::Base` delegators |
291
+
292
+ **Gate:** full CI green.
293
+
294
+ ---
295
+
296
+ ## PR 3d — Migrate RSpec Bridge and `from_base` References
297
+
298
+ **Releasable independently. No production behavior changes.**
299
+
300
+ - `spec/integration/git/repository/*_spec.rb` — replace `from_base` setup
301
+ with `Git::Repository.open(…)` or `ExecutionContext::Repository.from_hash`.
302
+ - `spec/unit/git/execution_context/repository_spec.rb` — remove `from_base`
303
+ and `base_object` test contexts; keep all other execution context coverage.
304
+ - `spec/unit/git/repository/remote_operations_spec.rb` — remove the
305
+ `base_object:` instance double.
306
+ - `spec/integration/git/branches_spec.rb`, `worktrees_spec.rb` — two changes
307
+ each:
308
+ 1. Remove the "initialized via `Git::Base`" context block entirely.
309
+ 2. In the surviving "initialized via `Git::Repository`" context, replace the
310
+ `let(:execution_context) { Git::ExecutionContext::Repository.from_base(repo) }`
311
+ setup with `ExecutionContext::Repository.from_hash` or
312
+ `Git::Repository.open`.
313
+ - `spec/integration/git/commands/init_spec.rb`, `clone_spec.rb` — replace
314
+ `Git::Lib.new(nil)` with a proper `ExecutionContext::Global` or
315
+ `ExecutionContext::Repository`.
316
+
317
+ **Gate:** full CI green; no spec references `from_base` or `base_object`.
318
+
319
+ ---
320
+
321
+ ## PR 4 — Atomic Removal
322
+
323
+ **Breaking change. Targets `main` (v5.x only). Cannot be further decomposed.**
324
+
325
+ With all callers rewired (1a–1c), docs updated (2a–2c), and tests migrated
326
+ (3a–3d), this PR is the single deletion event. Unloading `Git::Base` /
327
+ `Git::Lib` is itself the breaking change, so it must happen here — after every
328
+ test that references those constants (3a–3d) has been migrated.
329
+
330
+ **Remove the legacy `require` lines from `lib/git.rb`**
331
+
332
+ File: `lib/git.rb`
333
+ - Remove `require 'git/lib'`.
334
+ - Remove `require 'git/base'`.
335
+
336
+ After this, `Git::Base` and `Git::Lib` are no longer loaded at runtime.
337
+
338
+ **Remove the `ExecutionContext::Repository` bridge**
339
+
340
+ File: `lib/git/execution_context/repository.rb`
341
+ - Remove `base_object:` keyword and `@base_object = base_object` from `#initialize`.
342
+ - Remove `attr_reader :base_object`.
343
+ - Remove `base_object: @base_object` from `dup_with`.
344
+ - Delete the `.from_base` class method.
345
+ - Update `#initialize` YARD docs to drop the `base_object` param entry.
346
+
347
+ **Delete legacy class files**
348
+ - Delete `lib/git/lib.rb`.
349
+ - Delete `lib/git/base.rb`.
350
+
351
+ ---
352
+
353
+ ## Done-Gates (All Must Pass Before PR 4 Merges)
354
+
355
+ | Gate | How to verify |
356
+ | --- | --- |
357
+ | `lib/git/lib.rb` deleted | `ls lib/git/lib.rb` → not found |
358
+ | `lib/git/base.rb` deleted | `ls lib/git/base.rb` → not found |
359
+ | No `base_object` / `from_base` in `ExecutionContext::Repository` | Read file |
360
+ | No `require 'git/lib'` / `require 'git/base'` anywhere in `lib/` | `grep -rEn "require 'git/(lib|base)'" lib/` returns nothing |
361
+ | No runtime `Git::Lib` / `Git::Base` references in `lib/` | `grep -rEn 'Git::Lib|Git::Base' lib/` returns no code lines |
362
+ | `Git.open`, `.clone`, `.init`, `.bare` return `Git::Repository` and work | Existing RSpec integration suite |
363
+ | `Git.default_branch`, `Git.git_version` work without `Git::Lib` | Existing unit + integration specs |
364
+ | Full RSpec suite green | `bundle exec rake spec:parallel` |
365
+ | Full Test::Unit suite green | `bundle exec rake test:parallel` |
366
+ | Linters pass | `bundle exec rake rubocop yard build` |
@@ -0,0 +1,107 @@
1
+ # Beta Release Process
2
+
3
+ ## Why This Is a Manual Process
4
+
5
+ Pre-releases of the `git` gem (e.g., `5.0.0.beta.1`) are published manually rather
6
+ than through the normal release-please automation. This is necessary because:
7
+
8
+ - **release-please does not support Ruby pre-release version formats** such as
9
+ `5.0.0.beta.1`. release-please follows SemVer prerelease formatting and produces
10
+ versions with a dash separator (e.g., `5.0.0-beta.0`), which is incompatible with
11
+ RubyGems' dotted prerelease format (`5.0.0.beta.X`). For this reason,
12
+ `.release-please-config.json` explicitly sets `"prerelease": false` — there is no
13
+ configuration that makes release-please produce the right format.
14
+ - **We want release-please to keep accumulating commits toward the final `5.0.0`
15
+ release.** If we merged a release-please PR for a beta version, it would update
16
+ `.release-please-manifest.json` and lose the record of changes that should appear
17
+ in the `5.0.0` changelog. By managing betas manually and leaving the manifest
18
+ untouched, release-please continues to build the `5.0.0` release PR in the
19
+ background.
20
+ - **Beta tags use a `pre/` prefix** (e.g., `pre/v5.0.0.beta.1`) so that
21
+ release-please does not match them when searching for the last release SHA. This
22
+ keeps the change accumulation correct.
23
+
24
+ ---
25
+
26
+ ## Beta Release Checklist
27
+
28
+ ### 0. Set the beta number
29
+
30
+ Set `BETA` once at the start of each session. All commands below use it.
31
+
32
+ ```bash
33
+ BETA=2 # replace with the actual beta number
34
+ ```
35
+
36
+ > **Note:** Steps 1–3 are done before merging the release PR; steps 4–6 are done
37
+ > after. You will need to re-set `BETA` if you start a new shell session between
38
+ > the two phases.
39
+
40
+ ### 1. Prepare the release branch
41
+
42
+ ```bash
43
+ git switch main && git pull
44
+ git switch -c release/5.0.0.beta.${BETA}
45
+ ```
46
+
47
+ ### 2. Update files
48
+
49
+ - [ ] Bump `lib/git/version.rb`:
50
+
51
+ ```bash
52
+ sed -i "" "s/VERSION = '.*'/VERSION = '5.0.0.beta.${BETA}'/" lib/git/version.rb
53
+ ```
54
+
55
+ - [ ] Update the announcement in `README.md`:
56
+ - Change the heading date and version number
57
+ - Update the % complete estimate
58
+ - Update the RubyGems link to point to the new version
59
+
60
+ ### 3. Commit and open a PR
61
+
62
+ ```bash
63
+ git add lib/git/version.rb README.md
64
+ git commit -m "chore: release v5.0.0.beta.${BETA}"
65
+ git push -u origin release/5.0.0.beta.${BETA}
66
+ ```
67
+
68
+ Open a PR targeting `main` and merge it once CI passes.
69
+
70
+ > **Important:** Do **not** update `.release-please-manifest.json`. Leaving it at
71
+ > the last stable 4.x release version is what allows release-please to keep
72
+ > accumulating changes toward `5.0.0`.
73
+
74
+ ### 4. Tag the release commit
75
+
76
+ Tag before building to ensure the gem and the GitHub release are pinned to the
77
+ exact same commit. If `main` advances after the PR merges, building first and
78
+ tagging later would cause the published gem and tag to diverge.
79
+
80
+ ```bash
81
+ BETA=2 # re-set if this is a new shell session
82
+ git switch main && git pull
83
+ git tag pre/v5.0.0.beta.${BETA}
84
+ git push origin pre/v5.0.0.beta.${BETA}
85
+ ```
86
+
87
+ ### 5. Build, publish, and create a GitHub release
88
+
89
+ Check out the tag before building to guarantee the gem is built from the exact
90
+ tagged commit, not from whatever `main` happens to be at build time.
91
+
92
+ ```bash
93
+ git checkout pre/v5.0.0.beta.${BETA}
94
+ bundle exec rake build
95
+ gem push pkg/git-5.0.0.beta.${BETA}.gem
96
+
97
+ gh release create pre/v5.0.0.beta.${BETA} \
98
+ pkg/git-5.0.0.beta.${BETA}.gem \
99
+ --title "v5.0.0.beta.${BETA}" \
100
+ --notes "Beta ${BETA} pre-release of v5.0.0. See the README for details." \
101
+ --prerelease
102
+ ```
103
+
104
+ ### 6. Verify
105
+
106
+ - [ ] RubyGems: `https://rubygems.org/gems/git/versions/5.0.0.beta.${BETA}`
107
+ - [ ] GitHub release: `https://github.com/ruby-git/ruby-git/releases/tag/pre/v5.0.0.beta.${BETA}`