git 5.0.0.beta.1 → 5.0.0.beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +6 -0
  3. data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
  4. data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
  5. data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
  6. data/.github/skills/facade-implementation/SKILL.md +14 -0
  7. data/.github/skills/facade-test-conventions/SKILL.md +14 -0
  8. data/.rubocop.yml +5 -0
  9. data/README.md +51 -11
  10. data/UPGRADING.md +141 -0
  11. data/git.gemspec +5 -0
  12. data/lib/git/branch.rb +7 -18
  13. data/lib/git/branches.rb +2 -10
  14. data/lib/git/command_line/base.rb +10 -0
  15. data/lib/git/command_line/capturing.rb +5 -3
  16. data/lib/git/command_line/streaming.rb +5 -3
  17. data/lib/git/command_line.rb +3 -3
  18. data/lib/git/commands/base.rb +7 -6
  19. data/lib/git/commands/cat_file/batch.rb +6 -1
  20. data/lib/git/commands/cat_file/raw.rb +7 -1
  21. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
  22. data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
  23. data/lib/git/commands/update_ref/batch.rb +1 -1
  24. data/lib/git/commands/version.rb +5 -0
  25. data/lib/git/commands.rb +5 -7
  26. data/lib/git/config.rb +17 -0
  27. data/lib/git/config_entry_info.rb +106 -0
  28. data/lib/git/configuring.rb +665 -0
  29. data/lib/git/deprecation.rb +9 -0
  30. data/lib/git/diff.rb +4 -8
  31. data/lib/git/diff_path_status.rb +2 -13
  32. data/lib/git/diff_stats.rb +1 -9
  33. data/lib/git/execution_context/global.rb +3 -28
  34. data/lib/git/execution_context/repository.rb +30 -41
  35. data/lib/git/execution_context.rb +43 -24
  36. data/lib/git/log.rb +3 -9
  37. data/lib/git/object.rb +14 -21
  38. data/lib/git/parsers/config_entry.rb +110 -0
  39. data/lib/git/parsers/ls_remote.rb +79 -0
  40. data/lib/git/remote.rb +7 -20
  41. data/lib/git/repository/branching.rb +183 -12
  42. data/lib/git/repository/committing.rb +64 -68
  43. data/lib/git/repository/configuring.rb +208 -13
  44. data/lib/git/repository/context_helpers.rb +264 -0
  45. data/lib/git/repository/factories.rb +682 -0
  46. data/lib/git/repository/inspecting.rb +99 -0
  47. data/lib/git/repository/maintenance.rb +65 -0
  48. data/lib/git/repository/merging.rb +63 -1
  49. data/lib/git/repository/object_operations.rb +133 -35
  50. data/lib/git/repository/path_resolver.rb +1 -1
  51. data/lib/git/repository/remote_operations.rb +166 -21
  52. data/lib/git/repository/staging.rb +187 -23
  53. data/lib/git/repository/stashing.rb +39 -3
  54. data/lib/git/repository/status_operations.rb +21 -0
  55. data/lib/git/repository.rb +68 -129
  56. data/lib/git/stash.rb +2 -9
  57. data/lib/git/stashes.rb +2 -7
  58. data/lib/git/status.rb +8 -17
  59. data/lib/git/version.rb +2 -2
  60. data/lib/git/worktree.rb +2 -15
  61. data/lib/git/worktrees.rb +2 -15
  62. data/lib/git.rb +180 -77
  63. data/redesign/3_architecture_implementation.md +148 -111
  64. data/redesign/Phase 4 - Step A.md +360 -0
  65. data/redesign/beta_release.md +107 -0
  66. data/redesign/c1c2_audit.md +566 -0
  67. data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
  68. data/redesign/config_design.rb +501 -0
  69. metadata +19 -5
  70. data/lib/git/base.rb +0 -1204
  71. data/lib/git/lib.rb +0 -2855
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f42d907159502c20d7ba5a2e9545f202be91fb64bf2efcf5663c418acc99a396
4
- data.tar.gz: 42361a9dd11080081afddcd446131a4784acf1c7b38774a697026f00fbfb3dff
3
+ metadata.gz: 30866d1c683ae76cdb07eb526d9adecd45c8ec08bb53824c3a027984297ec256
4
+ data.tar.gz: a326ea01ed821d33c4edc60963cd2f6473ecaae2cad0daa82c06747456daa8dc
5
5
  SHA512:
6
- metadata.gz: 264ac7e0a0f8236143c1e95f4bc304b35bca810b7e3d898e8d747ebb7bbec69921a4bc9286899ff3314d95ae02680f349cfcb0f07dcc5d72885e044ab3841b28
7
- data.tar.gz: 3212f8816a84bb0be1131e3099e2b0e5fcf248217851b6bffd6176a442e84840e723cb8e5b12b20600d11f2277d4495b61a35bb3f45f3b0e54d5dda6bfc5958f
6
+ metadata.gz: a0f4cc725c36422a2afa046897d89bef4597fe8120215f83b22c9814d8c1817d4b795525c2aa4edd2c8d52bf617e36bba3b181d5a67af9067a5a6b859bbd6707
7
+ data.tar.gz: 15957da7f84ef75aa7ad78d6eb7eac4d96aab21b56c265749c5def8a7cb035b5fdce651a395a98ce30fccab3d62c5e66f479fae621d16227c1eb1468c4372f5c
@@ -17,6 +17,12 @@ and compatibility requirements see the
17
17
  ## Terminology & Writing Style
18
18
 
19
19
  - Use American English always. Avoid British English spellings and idioms.
20
+ - **Version strings** — use the most precise form appropriate to the context:
21
+ - `vN.x` (e.g. `v5.x`, `v4.x`) — the whole major series; use for compatibility
22
+ statements and upgrade guide scope ("v4.x code works on v5.x").
23
+ - `vN.0.0` (e.g. `v5.0.0`, `v6.0.0`) — a specific first release; use when
24
+ precision matters ("removed in v5.0.0", "the foundation delivered in v5.0.0").
25
+ - `vN.0` — avoid; ambiguous between "minor version 0" and "shorthand for vN.0.0".
20
26
  - **RuboCop** — correct capitalization when referring to the tool by name in prose,
21
27
  documentation, or comments.
22
28
  - **`rubocop`** — correct form when referring to the command-line executable or gem
@@ -0,0 +1,188 @@
1
+ ---
2
+ agent: agent
3
+ model: claude-sonnet-4.6
4
+ description: Address all unresolved Copilot review threads on the active pull request until there are no remaining unresolved review threads
5
+ ---
6
+
7
+ Address unresolved **Copilot** review threads on the active pull request. Ignore threads opened by human reviewers. Ask me for clarification or decisions as needed.
8
+
9
+ If any terminal script command fails, stop immediately. Do not continue the loop, do not resolve additional threads, and do not request a new review. Report: the failed command, exit code, and the most relevant stderr output. **Exception**: the reply POST in Step 3 is intentionally non-fatal — a 404 after a force-push is expected and the resolve step must still run.
10
+
11
+ ## Terminology
12
+
13
+ - **Review** — a top-level review submission by `copilot-pull-request-reviewer`, with a `submittedAt` timestamp and an optional summary body. A single review may contain zero or more Review Threads.
14
+ - **Review Thread** — an inline comment thread attached to a specific code location. Key fields: `id` (GraphQL node ID, e.g. `PRRT_…`), `isResolved` (manually resolved by a maintainer), `isOutdated` (the underlying code changed since the thread was created). Each thread has one or more comments; the first comment is Copilot's suggestion.
15
+ - **Check Run** — a standard CI status object on the HEAD commit. Note: Copilot Reviews do **not** create a Check Run; use the Reviews API to detect completion instead.
16
+
17
+ ## Before the Loop
18
+
19
+ Run in terminal to establish `OWNER`, `REPO`, and `PR_NUMBER` for use throughout:
20
+
21
+ ```bash
22
+ set -euo pipefail
23
+ OWNER=$(gh repo view --json owner --jq '.owner.login')
24
+ REPO=$(gh repo view --json name --jq '.name')
25
+ PR_NUMBER=$(gh pr view --json number --jq '.number')
26
+ : "${OWNER:?failed to resolve OWNER}"
27
+ : "${REPO:?failed to resolve REPO}"
28
+ : "${PR_NUMBER:?failed to resolve PR_NUMBER}"
29
+ ```
30
+
31
+ Fetch unresolved, non-outdated Copilot Review Threads:
32
+
33
+ ```bash
34
+ set -euo pipefail
35
+ : "${OWNER:?missing OWNER}"
36
+ : "${REPO:?missing REPO}"
37
+ : "${PR_NUMBER:?missing PR_NUMBER}"
38
+ threads_json=$(gh api graphql -f query='
39
+ query($owner:String!,$repo:String!,$pr:Int!){
40
+ repository(owner:$owner,name:$repo){
41
+ pullRequest(number:$pr){
42
+ reviewThreads(first:100){nodes{id isResolved isOutdated path
43
+ comments(first:1){nodes{databaseId author{login} createdAt body}}}}}}
44
+ }' -f owner="$OWNER" -f repo="$REPO" -F pr="$PR_NUMBER" \
45
+ --jq '.data.repository.pullRequest.reviewThreads.nodes')
46
+ if [[ $(echo "$threads_json" | jq 'length') -ge 100 ]]; then
47
+ echo "Error: the PR already has 100 threads or more. Aborting."
48
+ exit 1
49
+ fi
50
+ echo "$threads_json" | jq '[.[] |
51
+ select(.isResolved==false) |
52
+ select(.isOutdated==false) |
53
+ select(.comments.nodes[0].author.login=="copilot-pull-request-reviewer")]'
54
+ ```
55
+
56
+ - **Results non-empty** → proceed directly to the iteration loop.
57
+ - **Results empty** → record `REVIEW_REQUESTED_AT` (current UTC, `YYYY-MM-DDTHH:MM:SSZ`, e.g. `2026-06-16T12:00:00Z`), request a new Copilot Review using the `mcp_github_mcp_se_request_copilot_review` tool (owner, repo, pullNumber), then jump to the **Wait for Review** section below.
58
+
59
+ ## Iteration Loop
60
+
61
+ Repeat up to **${input:maxIterations:5}** iterations:
62
+
63
+ ### 1. Address threads
64
+
65
+ Re-fetch unresolved, non-outdated Copilot Review Threads using the same `gh api graphql` query from Before the Loop. Group by file. For each file, read it once and address all its threads in that single pass:
66
+ - Validate each suggestion before accepting it.
67
+ - If a suggestion is invalid or out of scope: reply explaining why, then resolve the thread without changing code.
68
+ - Otherwise: implement the change using TDD where possible; ensure test coverage.
69
+
70
+ Skip any thread where `isOutdated` is true — the code it references has already changed; Copilot will re-evaluate it in the next Review.
71
+
72
+ After all threads are addressed, run `rake`. If it fails, capture `rake 2>&1 | tail -n 50` and fix the failure before continuing.
73
+
74
+ ### 2. Commit and push
75
+
76
+ Amend each change into the most relevant existing commit on the branch based on file name. If a change spans multiple commits or doesn't map clearly to one, ask me which commit to amend into (or whether to create a new commit). Confirm the working tree is clean and `rake` passes, then force push.
77
+
78
+ ### 3. Reply and resolve
79
+
80
+ For each addressed thread object from the unresolved-threads query, in the same `run_in_terminal` script block that performs reply/resolve, export:
81
+
82
+ - `COMMENT_DBID=.comments.nodes[0].databaseId`
83
+ - `THREAD_ID=.id`
84
+ - `EXPLANATION` to your plain-language fix summary for that thread
85
+
86
+ Set the per-thread variables (`COMMENT_DBID`, `THREAD_ID`, `EXPLANATION`) immediately before running the commands below — do not rely on them surviving from a prior terminal invocation.
87
+
88
+ Then post a reply and resolve it:
89
+
90
+ ```bash
91
+ set -euo pipefail
92
+ : "${OWNER:?missing OWNER}"
93
+ : "${REPO:?missing REPO}"
94
+ : "${COMMENT_DBID:?missing COMMENT_DBID}"
95
+ : "${EXPLANATION:?missing EXPLANATION}"
96
+
97
+ # Reply (COMMENT_DBID = databaseId of the thread's first comment)
98
+ # Build JSON via jq to safely handle quotes/newlines/special chars in EXPLANATION.
99
+ # EXPLANATION should contain the full reply text (e.g. "Fixed: ..." or "Not addressing this because...").
100
+ BODY_JSON=$(jq -n --arg body "$EXPLANATION" '{body:$body}')
101
+ # Non-fatal: a force-push can mark threads as outdated, causing the REST reply to return 404.
102
+ # Always continue to the GraphQL resolve step regardless.
103
+ gh api "repos/$OWNER/$REPO/pulls/comments/$COMMENT_DBID/replies" \
104
+ -X POST --input - <<<"$BODY_JSON" \
105
+ || echo "Warning: reply POST failed (thread may be outdated after force-push) — skipping reply, will still resolve"
106
+
107
+ : "${THREAD_ID:?missing THREAD_ID}"
108
+
109
+ # Resolve (THREAD_ID = GraphQL node id, e.g. PRRT_...)
110
+ gh api graphql \
111
+ -f query='mutation($id:ID!){resolveReviewThread(input:{threadId:$id}){thread{isResolved}}}' \
112
+ -f id="$THREAD_ID"
113
+ ```
114
+
115
+ ### 4. Request review
116
+
117
+ **If this was the ${input:maxIterations:5}th iteration**, skip steps 4 and 6 entirely — go directly to step 5 (Report) and then produce the Final Report.
118
+
119
+ Otherwise, capture `REVIEW_REQUESTED_AT` by running `date -u +%Y-%m-%dT%H:%M:%SZ` in the terminal immediately before requesting the review. Then request a new Copilot Review using the `mcp_github_mcp_se_request_copilot_review` tool (owner, repo, pullNumber).
120
+
121
+ ### 5. Report
122
+
123
+ List what was addressed and how each issue was resolved.
124
+
125
+ ### 6. Wait
126
+
127
+ Jump to the **Wait for Review** section below. Return here to begin the next iteration once the new Copilot Review has been submitted.
128
+
129
+ ## Wait for Review
130
+
131
+ **[BLOCKING — do not proceed until complete]** Poll for a new Copilot Review submission using the Reviews API. A Review with `submittedAt >= REVIEW_REQUESTED_AT` is the authoritative completion signal — it fires even when Copilot produces zero Review Threads.
132
+
133
+ Run the following script via `run_in_terminal` (sync mode, timeout 750000 ms). Set the four variables on the first line to their actual values. On success, capture the script's last output line; on failure, follow the global failure-reporting rule (failed command, exit code, relevant stderr):
134
+
135
+ ```bash
136
+ set -euo pipefail
137
+ # Replace with actual values. REVIEW_REQUESTED_AT = output of `date -u +%Y-%m-%dT%H:%M:%SZ` captured just before requesting the review.
138
+ OWNER="ruby-git"; REPO="ruby-git"; PR_NUMBER="1439"; REVIEW_REQUESTED_AT="2026-06-16T12:00:00Z"
139
+ : "${OWNER:?missing OWNER}"
140
+ : "${REPO:?missing REPO}"
141
+ : "${PR_NUMBER:?missing PR_NUMBER}"
142
+ : "${REVIEW_REQUESTED_AT:?missing REVIEW_REQUESTED_AT}"
143
+ START=$(date +%s)
144
+ for i in $(seq 1 60); do
145
+ new_review=$(gh pr view "$PR_NUMBER" --repo "$OWNER/$REPO" --json reviews \
146
+ --jq "[.reviews[] | select(.author.login==\"copilot-pull-request-reviewer\") | select(.submittedAt != null) | select(.submittedAt | fromdateiso8601 >= (\"$REVIEW_REQUESTED_AT\" | fromdateiso8601))] | length") \
147
+ || { rc=$?; echo "Error: gh pr view failed (exit $rc)"; exit $rc; }
148
+ if [[ -n "$new_review" && "$new_review" -gt 0 ]]; then
149
+ raw_nodes=$(gh api graphql -f query='
150
+ query($owner:String!,$repo:String!,$pr:Int!){
151
+ repository(owner:$owner,name:$repo){
152
+ pullRequest(number:$pr){
153
+ reviewThreads(first:100){nodes{isResolved isOutdated comments(first:1){nodes{author{login}}}}}
154
+ }
155
+ }
156
+ }' -f owner="$OWNER" -f repo="$REPO" -F pr="$PR_NUMBER" \
157
+ --jq '.data.repository.pullRequest.reviewThreads.nodes') \
158
+ || { rc=$?; echo "Error: gh api graphql failed (exit $rc)"; exit $rc; }
159
+ if [[ $(echo "$raw_nodes" | jq 'length') -ge 100 ]]; then
160
+ echo "Error: the PR already has 100 threads or more. Aborting."
161
+ exit 1
162
+ fi
163
+ count=$(echo "$raw_nodes" | jq "[.[] |
164
+ select(.isResolved==false) |
165
+ select(.isOutdated==false) |
166
+ select(.comments.nodes[0].author.login==\"copilot-pull-request-reviewer\")] | length")
167
+ echo "done: $count threads" # count of all unresolved non-outdated Copilot Review Threads
168
+ exit 0
169
+ fi
170
+ elapsed=$(( $(date +%s) - START ))
171
+ echo "Waiting for Copilot Review to complete... (${elapsed}s elapsed)"
172
+ sleep 10
173
+ done
174
+ echo "timed out after 60 polls"
175
+ exit 1
176
+ ```
177
+
178
+ **If `timed out after 60 polls`**: stop immediately and ask me whether to re-request the review and retry, or abort.
179
+
180
+ **If `done: 0 threads`**: the loop is complete — exit.
181
+
182
+ **If `done: N threads`** (N > 0): begin the next iteration.
183
+
184
+ ## Final Report
185
+
186
+ - Total iterations completed
187
+ - Total threads addressed
188
+ - Unresolved threads in the final Copilot review (should be 0 if the loop exited cleanly)
@@ -0,0 +1,22 @@
1
+ # Keyword-arg remediation list
2
+
3
+ The following facade methods are known to use `**opts`/`**` keyword-splat where
4
+ the legacy `Git::Base` or `Git::Lib` predecessor used a positional options hash
5
+ (`opts = {}`). Each is a candidate `legacy-contract` violation that must be
6
+ resolved before `Git.open`/`.clone`/`.init`/`.bare` are changed to return
7
+ `Git::Repository`: either fix the signature or
8
+ record an explicit `5.x-native` justification.
9
+
10
+ | Facade method | Current signature | Expected classification | Action needed |
11
+ | --- | --- | --- | --- |
12
+ | `Git::Repository::Staging#add` | `add(paths = '.', **)` | `legacy-contract` | Verify against 4.x `Git::Lib#add`; change to `opts = {}` if legacy |
13
+ | `Git::Repository::Staging#reset` | `reset(commitish = nil, **)` | `legacy-contract` | Verify against 4.x `Git::Lib#reset`; change to `opts = {}` if legacy |
14
+ | `Git::Repository::Committing#commit` | `commit(message = nil, **opts)` | `legacy-contract` | Verify against 4.x `Git::Base#commit`; change to `opts = {}` if legacy |
15
+ | `Git::Repository::Committing#commit_all` | `commit_all(*, **)` | `legacy-contract` | Verify against 4.x `Git::Base#commit_all`; change to `opts = {}` if legacy |
16
+ | `Git::Repository::Committing#commit_tree` | `commit_tree(tree, **opts)` | needs classification | Classify; if 5.x-native confirm; if legacy-contract fix signature |
17
+ | `Git::Repository::Committing#write_and_commit_tree` | `write_and_commit_tree(**)` | needs classification | Classify; if 5.x-native confirm; if legacy-contract fix signature |
18
+ | `Git::Repository::Branching#branch_delete` | `branch_delete(*branches, **options)` | needs classification | Verify against 4.x `Git::Base#branch_delete`; classify and fix or confirm |
19
+ | `Git::Repository::Inspecting#fsck` | `fsck(*objects, **)` | needs classification | Verify against 4.x `Git::Lib#fsck`; classify and fix or confirm |
20
+
21
+ This list is seeded from a static scan of `lib/git/repository/**/*.rb` and may be
22
+ incomplete. A full public-method inventory is required before closing the sweep.
@@ -18,7 +18,6 @@ preserve backward compatibility within the migration window.
18
18
 
19
19
  ## Contents
20
20
 
21
- - [Contents](#contents)
22
21
  - [How to use this skill](#how-to-use-this-skill)
23
22
  - [Prerequisites](#prerequisites)
24
23
  - [Related skills](#related-skills)
@@ -28,6 +27,7 @@ preserve backward compatibility within the migration window.
28
27
  - [Pattern B — `Git::Lib`-only (public-by-exposure)](#pattern-b--gitlib-only-public-by-exposure)
29
28
  - [Pattern C — `Git::Base`-only (no `Git::Lib` method)](#pattern-c--gitbase-only-no-gitlib-method)
30
29
  - [Signature compatibility policy](#signature-compatibility-policy)
30
+ - [Keyword-arg remediation list](#keyword-arg-remediation-list)
31
31
  - [Determining the option allowlist](#determining-the-option-allowlist)
32
32
  - [Workflow](#workflow)
33
33
  - [Branch setup](#branch-setup)
@@ -180,13 +180,29 @@ Use this policy in both extraction mode and review mode:
180
180
 
181
181
  Rules:
182
182
 
183
- 1. Default classification is `legacy-contract` when a legacy predecessor exists.
184
- 2. `5.x-native` requires explicit justification in the plan/review notes.
185
- 3. For `legacy-contract` methods, preserve the exact 4.x signature (including
186
- rare `**opts` signatures). For `5.x-native` methods, use `opts = {}` style
187
- for consistency.
188
- 4. For `legacy-contract` methods, tests must prove the expected call shape,
189
- not only command delegation.
183
+ 1. `legacy-contract` methods preserve the exact 4.x call shape verbatim,
184
+ including rare `**opts` signatures; never alter the parameter list when a
185
+ legacy predecessor exists.
186
+ 2. `5.x-native` methods use `opts = {}` style for consistency; a broader kwargs
187
+ migration is deferred to v6.x so it can be applied uniformly across the
188
+ entire public API.
189
+ 3. Every extracted method must be explicitly classified as `legacy-contract` or
190
+ `5.x-native` before the PR is opened.
191
+ 4. The classification must appear in the PR description and in the extracted
192
+ method's YARD `@note`. Example:
193
+
194
+ ```ruby
195
+ # @note Signature compatibility: legacy-contract — preserves the exact Git::Base#foo 4.x call shape.
196
+ ```
197
+
198
+ ## Keyword-arg remediation list
199
+
200
+ See [KEYWORD_ARG_REMEDIATION.md](KEYWORD_ARG_REMEDIATION.md) for the initial list
201
+ of facade methods that use `**opts`/`**` keyword-splat where the legacy predecessor
202
+ used `opts = {}`. Each method in this list must be resolved before
203
+ `Git.open`/`.clone`/`.init`/`.bare` are changed to return `Git::Repository`:
204
+ either fix the signature to match the `legacy-contract` classification, or
205
+ record an explicit `5.x-native` justification.
190
206
 
191
207
  ## Determining the option allowlist
192
208
 
@@ -409,15 +425,13 @@ end
409
425
 
410
426
  ```ruby
411
427
  # lib/git/base.rb
412
- def add_remote(name, url, opts = {})
413
- repository.add_remote(name, url, opts)
428
+ def remote_add(name, url, opts = {})
429
+ facade_repository.remote_add(name, url, opts)
414
430
  end
415
431
  ```
416
432
 
417
- (The exact accessor `repository`, `@repository`, `self.repository` — depends
418
- on how `Git::Base` and `Git::Lib` hold their reference to the new
419
- `Git::Repository` instance during the migration window. Match the existing
420
- pattern used by other migrated methods.)
433
+ (Use `facade_repository` to access the `Git::Repository` facade instance this
434
+ is the accessor used by all migrated methods in `Git::Base`.)
421
435
 
422
436
  After delegation is in place, verify:
423
437
 
@@ -17,6 +17,7 @@ for the five facade responsibilities this layer is designed around.
17
17
 
18
18
  ## Contents
19
19
 
20
+ - [How to use this skill](#how-to-use-this-skill)
20
21
  - [Related skills](#related-skills)
21
22
  - [Input](#input)
22
23
  - [Existing facade source](#existing-facade-source)
@@ -27,6 +28,19 @@ for the five facade responsibilities this layer is designed around.
27
28
  - [Workflow](#workflow)
28
29
  - [Output](#output)
29
30
 
31
+ ## How to use this skill
32
+
33
+ Attach this file to your Copilot Chat context, then invoke with the facade method
34
+ to scaffold, update, or review. Examples:
35
+
36
+ ```text
37
+ Using the Facade Implementation skill, scaffold Git::Repository::Staging#add.
38
+ ```
39
+
40
+ ```text
41
+ Facade Implementation review: Git::Repository::Committing#commit.
42
+ ```
43
+
30
44
  ## Related skills
31
45
 
32
46
  - [Facade Test Conventions](../facade-test-conventions/SKILL.md) — unit and
@@ -10,6 +10,7 @@ methods on `Git::Repository::*` modules.
10
10
 
11
11
  ## Contents
12
12
 
13
+ - [How to use this skill](#how-to-use-this-skill)
13
14
  - [Related skills](#related-skills)
14
15
  - [Input](#input)
15
16
  - [Reference](#reference)
@@ -30,6 +31,19 @@ methods on `Git::Repository::*` modules.
30
31
  - [When writing new facade tests](#when-writing-new-facade-tests)
31
32
  - [When reviewing existing facade tests](#when-reviewing-existing-facade-tests)
32
33
 
34
+ ## How to use this skill
35
+
36
+ Attach this file to your Copilot Chat context, then invoke with the spec file(s)
37
+ to write or review. Include the corresponding facade module for context. Examples:
38
+
39
+ ```text
40
+ Using the Facade Test Conventions skill, scaffold tests for Git::Repository::Staging.
41
+ ```
42
+
43
+ ```text
44
+ Facade Test Conventions review: spec/unit/git/repository/committing_spec.rb.
45
+ ```
46
+
33
47
  ## Related skills
34
48
 
35
49
  - [RSpec Unit Testing Standards](../rspec-unit-testing-standards/SKILL.md) — baseline
data/.rubocop.yml CHANGED
@@ -45,6 +45,11 @@ Metrics/BlockLength:
45
45
  - "*.gemspec"
46
46
  - "lib/git/commands/**/*.rb"
47
47
 
48
+ # lib/git.rb is the gem's main entry-point module and is expected to be long
49
+ Metrics/ModuleLength:
50
+ Exclude:
51
+ - "lib/git.rb"
52
+
48
53
  # Don't force every test class to be described
49
54
  Style/Documentation:
50
55
  Exclude:
data/README.md CHANGED
@@ -30,6 +30,7 @@ Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?log
30
30
  - [Ruby Version Support Policy](#ruby-version-support-policy)
31
31
  - [Git Version Support Policy](#git-version-support-policy)
32
32
  - [📢 Project Announcements 📢](#-project-announcements-)
33
+ - [2026-06-25: v5.0.0.beta.2 Released](#2026-06-25-v500beta2-released)
33
34
  - [2026-06-04: v5.0.0.beta.1 Released](#2026-06-04-v500beta1-released)
34
35
  - [2026-01-07: AI Policy Introduced](#2026-01-07-ai-policy-introduced)
35
36
  - [2025-07-09: Architectural Redesign](#2025-07-09-architectural-redesign)
@@ -52,7 +53,7 @@ Get started by obtaining a repository object by:
52
53
  [Git.clone](https://rubydoc.info/gems/git/Git#clone-class_method)
53
54
 
54
55
  Methods that can be called on a repository object are documented in
55
- [Git::Base](https://rubydoc.info/gems/git/Git/Base)
56
+ [Git::Repository](https://rubydoc.info/gems/git/Git/Repository)
56
57
 
57
58
  ## Install
58
59
 
@@ -87,8 +88,8 @@ All functionality for this gem starts with the top-level
87
88
  non-repo scoped `git` commands such as `config`.
88
89
 
89
90
  The `Git` module also has factory methods such as `open`, `clone`, and `init` which
90
- return a [`Git::Base`](https://rubydoc.info/gems/git/Git/Base) object. The
91
- `Git::Base` object is used to run repo-specific `git` commands such as `add`,
91
+ return a [`Git::Repository`](https://rubydoc.info/gems/git/Git/Repository) object. The
92
+ `Git::Repository` object is used to run repo-specific `git` commands such as `add`,
92
93
  `commit`, `push`, and `log`.
93
94
 
94
95
  Clone, read status, and log:
@@ -416,8 +417,8 @@ repo.merge([branch1, branch2])
416
417
 
417
418
  repo.merge_base('branch1', 'branch2')
418
419
 
419
- r = repo.add_remote(name, uri) # Git::Remote
420
- r = repo.add_remote(name, Git::Base) # Git::Remote
420
+ r = repo.remote_add(name, uri) # Git::Remote
421
+ r = repo.remote_add(name, other_repo) # Git::Remote (other_repo is a Git::Repository instance)
421
422
 
422
423
  repo.remotes # array of Git::Remotes
423
424
  repo.remote(name).fetch
@@ -437,12 +438,12 @@ repo.fetch('origin', {:'update-head-ok' => true})
437
438
  repo.pull
438
439
  repo.pull(Git::Repo, Git::Branch) # fetch and a merge
439
440
 
440
- repo.add_tag('tag_name') # returns Git::Tag
441
- repo.add_tag('tag_name', 'object_reference')
442
- repo.add_tag('tag_name', 'object_reference', {:options => 'here'})
443
- repo.add_tag('tag_name', {:options => 'here'})
441
+ repo.tag_add('tag_name') # returns Git::Object::Tag
442
+ repo.tag_add('tag_name', 'object_reference')
443
+ repo.tag_add('tag_name', 'object_reference', {:options => 'here'})
444
+ repo.tag_add('tag_name', {:options => 'here'})
444
445
 
445
- repo.delete_tag('tag_name')
446
+ repo.tag_delete('tag_name')
446
447
 
447
448
  repo.repack
448
449
 
@@ -650,17 +651,56 @@ notes.
650
651
 
651
652
  ## 📢 Project Announcements 📢
652
653
 
654
+ ### 2026-06-25: v5.0.0.beta.2 Released
655
+
656
+ The architectural redesign is approximately **90% complete** and we have published
657
+ [`git v5.0.0.beta.2`](https://rubygems.org/gems/git/versions/5.0.0.beta.2) as our
658
+ second pre-release.
659
+
660
+ **To try the beta**, add the pre-release version to your `Gemfile`:
661
+
662
+ ```ruby
663
+ gem 'git', '~> 5.0.0.beta'
664
+ ```
665
+
666
+ Or install it directly:
667
+
668
+ ```sh
669
+ gem install git --pre
670
+ ```
671
+
672
+ The intent is full backward compatibility with v4.x, but given the size and scope of
673
+ the redesign, some incompatibilities may exist. Please give the latest beta a try and
674
+ [open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
675
+ unexpected — your feedback helps us ship a solid v5.0.0.
676
+
677
+ See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
678
+
653
679
  ### 2026-06-04: v5.0.0.beta.1 Released
654
680
 
655
681
  The architectural redesign is approximately **65% complete** and we have published
656
- [`git 5.0.0.beta.1`](https://rubygems.org/gems/git/versions/5.0.0.beta.1) as our
682
+ [`git v5.0.0.beta.1`](https://rubygems.org/gems/git/versions/5.0.0.beta.1) as our
657
683
  first pre-release.
658
684
 
685
+ **To try the beta**, add the pre-release version to your `Gemfile`:
686
+
687
+ ```ruby
688
+ gem 'git', '~> 5.0.0.beta'
689
+ ```
690
+
691
+ Or install it directly:
692
+
693
+ ```sh
694
+ gem install git --pre
695
+ ```
696
+
659
697
  The intent is full backward compatibility with 4.x, but given the size and scope of
660
698
  the redesign, some incompatibilities may exist. Please give the latest beta a try and
661
699
  [open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
662
700
  unexpected — your feedback helps us ship a solid 5.0.0.
663
701
 
702
+ See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
703
+
664
704
  ### 2026-01-07: AI Policy Introduced
665
705
 
666
706
  We have adopted a formal [AI Policy](AI_POLICY.md) to clarify expectations for
data/UPGRADING.md ADDED
@@ -0,0 +1,141 @@
1
+ # Upgrading the `git` Gem
2
+
3
+ > **⚠️ Work in progress — v5.0.0 is currently in beta.**
4
+ > This document is updated incrementally as development continues and is not yet
5
+ > complete. If you encounter a compatibility problem not covered here, please
6
+ > [open an issue](https://github.com/ruby-git/ruby-git/issues).
7
+
8
+ - [Upgrading from v4.x to v5.x](#upgrading-from-v4x-to-v5x)
9
+ - [Overview](#overview)
10
+ - [`Git::Lib` removal](#gitlib-removal)
11
+ - [Methods with a direct replacement](#methods-with-a-direct-replacement)
12
+ - [Methods with no replacement](#methods-with-no-replacement)
13
+ - [Internal plumbing methods (no replacement)](#internal-plumbing-methods-no-replacement)
14
+ - [Deprecated methods](#deprecated-methods)
15
+
16
+ ## Upgrading from v4.x to v5.x
17
+
18
+ ### Overview
19
+
20
+ The primary goal of v5.0.0 is to move everyone onto a new internal architecture
21
+ while keeping the existing v4.x API working. The vast majority of v4.x code
22
+ requires no changes to run on v5.x.
23
+
24
+ The new architecture delivered in v5.0.0 is the foundation for future API
25
+ improvements across upcoming major releases.
26
+
27
+ The new architecture introduces a layered design (`Git::Commands`, `Git::Repository`,
28
+ and associated parsers). Compatibility shims — deprecated forwarding methods that map
29
+ old call patterns to the new API — ensure that v4.x code continues to work. These
30
+ shims emit deprecation warnings that tell you exactly what to change and what will be
31
+ eliminated in a future major release (most likely v6.0.0).
32
+
33
+ Hard breaks are limited to a small number of methods that had no safe
34
+ migration path — these are described in detail below.
35
+
36
+ Our intent is to make upgrading to v5.x as smooth as possible. For information
37
+ on how to suppress or configure deprecation warnings, see the
38
+ [Deprecations](README.md#deprecations) section of the README.
39
+
40
+ ---
41
+
42
+ ### `Git::Lib` removal
43
+
44
+ The object returned by `Git.open`, `Git.clone`, and `Git.init` — the object
45
+ you call methods on to interact with your repository — inadvertently exposed
46
+ a `#lib` method that gave access to `Git::Lib`, the gem's internal
47
+ implementation class. This was never intended to be public; it was an
48
+ implementation detail that leaked out. `Git::Lib` is removed in v5.0.0.
49
+
50
+ Calling `#lib` on the repository object returns `self` with a deprecation
51
+ warning so that existing `g.lib.*` call chains continue to work during
52
+ migration. The `#lib` method itself is removed in v6.0.0. Calls to methods
53
+ that have no replacement (see below) raise `NoMethodError` with an
54
+ informative message.
55
+
56
+ Most public behavior previously accessible via `g.lib.*` is available
57
+ directly on the repository object (i.e., `g.*`). A small number of methods
58
+ have no replacement — see below. The sections below list every affected method.
59
+
60
+ #### Methods with a direct replacement
61
+
62
+ All of the following `g.lib.*` calls work in v5.x but emit a deprecation
63
+ warning. Migrate to the replacement shown to silence the `g.lib.*` deprecation
64
+ warning; note that some replacements are themselves deprecated — see the table
65
+ notes for the final migration target.
66
+
67
+ | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
68
+ |---------------------------------------------------|-------------|
69
+ | `g.lib.config_get(name)` | `g.config(name)` |
70
+ | `g.lib.config_list` | `g.config` |
71
+ | `g.lib.config_set(name, value)` | `g.config(name, value)` |
72
+ | `g.lib.git_version` | `g.git_version` |
73
+ | `g.lib.global_config_get(name)` | `g.global_config(name)` |
74
+ | `g.lib.global_config_list` | `g.global_config` |
75
+ | `g.lib.global_config_set(name, value)` | `g.global_config(name, value)` |
76
+ | `g.lib.parse_config(file)` | `g.config(file: file)` |
77
+ | `g.lib.stash_list` | `g.stash_list` *(also deprecated — use `g.stashes_all`)* |
78
+ | `g.lib.unmerged` | `g.unmerged` |
79
+ | `g.lib.change_head_branch(name)` | `g.change_head_branch(name)` |
80
+ | `g.lib.branch_current` | `g.current_branch` |
81
+ | `g.lib.ls_remote(location, opts)` | `g.ls_remote(location, opts)` |
82
+ | `g.lib.current_branch_state` | `g.current_branch_state` |
83
+
84
+ > **Note — `current_branch_state` return type change:** `g.lib.current_branch_state`
85
+ > returned a `Git::Lib::HeadState` (a mutable `Struct`). `g.current_branch_state`
86
+ > returns a `Git::Repository::Branching::HeadState` (an immutable `Data` object).
87
+ > Both expose `.state` (`:active`, `:unborn`, or `:detached`) and `.name`. If your
88
+ > code relies on the struct being mutable or uses positional construction
89
+ > (`Git::Lib::HeadState.new(:active, 'main')`), update to keyword construction:
90
+ > `Git::Repository::Branching::HeadState.new(state: :active, name: 'main')`.
91
+
92
+ #### Methods with no replacement
93
+
94
+ | v4.x call | Notes |
95
+ |-----------|-------|
96
+ | `g.lib.list_files(ref_dir)` | Walked `.git/refs/` directly. Use `g.branches`, `g.tags`, or `g.remotes` instead. |
97
+
98
+ #### Internal plumbing methods (no replacement)
99
+
100
+ The following methods were technically public on `Git::Lib` but are internal
101
+ helpers with no plausible external use. They have no replacement in v5.0.0:
102
+
103
+ - `assert_args_are_not_options`
104
+ - `assert_valid_opts`
105
+ - `cat_file_object_meta`
106
+ - `command_capturing`
107
+ - `command_streaming`
108
+ - `each_cat_file_header`
109
+ - `handle_deprecated_path_option`
110
+ - `normalize_pathspecs`
111
+ - `parse_cat_file_meta`
112
+ - `parse_config_list`
113
+ - `process_commit_data`
114
+ - `validate_pathspec_types`
115
+
116
+ ---
117
+
118
+ ### Deprecated methods
119
+
120
+ The following methods are available in v5.x with deprecation warnings and
121
+ are removed in v6.0.0.
122
+
123
+ | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
124
+ |---------------------------------------------------|-------------|
125
+ | `g.config_get(name)` | `g.config(name)` |
126
+ | `g.config_list` | `g.config` |
127
+ | `g.config_set(name, value)` | `g.config(name, value)` |
128
+ | `g.global_config_get(name)` | `g.global_config(name)` |
129
+ | `g.global_config_list` | `g.global_config` |
130
+ | `g.global_config_set(name, value)` | `g.global_config(name, value)` |
131
+ | `g.parse_config(file)` | `g.config(file: file)` |
132
+ | `g.stash_list` | `g.stashes_all` |
133
+ | `g.add_remote(name, url, opts)` | `g.remote_add(name, url, opts)` |
134
+ | `g.remove_remote(name)` | `g.remote_remove(name)` |
135
+ | `g.set_remote_url(name, url)` | `g.remote_set_url(name, url)` |
136
+ | `g.add_tag(name, ...)` | `g.tag_add(name, ...)` |
137
+ | `g.delete_tag(name)` | `g.tag_delete(name)` |
138
+ | `include Git; config(...)` | `Git.open(Dir.pwd).config(...)` |
139
+ | `include Git; global_config(...)` | `Git.global_config(...)` |
140
+
141
+ ---
data/git.gemspec CHANGED
@@ -48,6 +48,11 @@ Gem::Specification.new do |spec|
48
48
  spec.add_development_dependency 'simplecov-rspec', '~> 0.4'
49
49
  spec.add_development_dependency 'test-unit', '~> 3.7'
50
50
 
51
+ if RUBY_ENGINE == 'truffleruby' && Gem::Version.new(RUBY_ENGINE_VERSION) < Gem::Version.new('34.0.0')
52
+ # i18n 1.15+ uses Fiber.[] (Ruby 3.2 Fiber storage) which TruffleRuby < 34.0.0 does not implement
53
+ spec.add_development_dependency 'i18n', '< 1.15'
54
+ end
55
+
51
56
  unless RUBY_PLATFORM == 'java' || RUBY_ENGINE == 'truffleruby'
52
57
  spec.add_development_dependency 'irb', '~> 1.16'
53
58
  spec.add_development_dependency 'redcarpet', '~> 3.6'