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.
- checksums.yaml +4 -4
- data/.github/copilot-instructions.md +6 -0
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
- data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
- data/.github/skills/extract-facade-from-base-lib/SKILL.md +28 -14
- data/.github/skills/facade-implementation/SKILL.md +14 -0
- data/.github/skills/facade-test-conventions/SKILL.md +14 -0
- data/.rubocop.yml +5 -0
- data/README.md +51 -11
- data/UPGRADING.md +141 -0
- data/git.gemspec +5 -0
- data/lib/git/branch.rb +7 -18
- data/lib/git/branches.rb +2 -10
- data/lib/git/command_line/base.rb +10 -0
- data/lib/git/command_line/capturing.rb +5 -3
- data/lib/git/command_line/streaming.rb +5 -3
- data/lib/git/command_line.rb +3 -3
- data/lib/git/commands/base.rb +7 -6
- data/lib/git/commands/cat_file/batch.rb +6 -1
- data/lib/git/commands/cat_file/raw.rb +7 -1
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/update_ref/batch.rb +1 -1
- data/lib/git/commands/version.rb +5 -0
- data/lib/git/commands.rb +5 -7
- data/lib/git/config.rb +17 -0
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +665 -0
- data/lib/git/deprecation.rb +9 -0
- data/lib/git/diff.rb +4 -8
- data/lib/git/diff_path_status.rb +2 -13
- data/lib/git/diff_stats.rb +1 -9
- data/lib/git/execution_context/global.rb +3 -28
- data/lib/git/execution_context/repository.rb +30 -41
- data/lib/git/execution_context.rb +43 -24
- data/lib/git/log.rb +3 -9
- data/lib/git/object.rb +14 -21
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/remote.rb +7 -20
- data/lib/git/repository/branching.rb +183 -12
- data/lib/git/repository/committing.rb +64 -68
- data/lib/git/repository/configuring.rb +208 -13
- data/lib/git/repository/context_helpers.rb +264 -0
- data/lib/git/repository/factories.rb +682 -0
- data/lib/git/repository/inspecting.rb +99 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +63 -1
- data/lib/git/repository/object_operations.rb +133 -35
- data/lib/git/repository/path_resolver.rb +1 -1
- data/lib/git/repository/remote_operations.rb +166 -21
- data/lib/git/repository/staging.rb +187 -23
- data/lib/git/repository/stashing.rb +39 -3
- data/lib/git/repository/status_operations.rb +21 -0
- data/lib/git/repository.rb +68 -129
- data/lib/git/stash.rb +2 -9
- data/lib/git/stashes.rb +2 -7
- data/lib/git/status.rb +8 -17
- data/lib/git/version.rb +2 -2
- data/lib/git/worktree.rb +2 -15
- data/lib/git/worktrees.rb +2 -15
- data/lib/git.rb +180 -77
- data/redesign/3_architecture_implementation.md +148 -111
- data/redesign/Phase 4 - Step A.md +360 -0
- data/redesign/beta_release.md +107 -0
- data/redesign/c1c2_audit.md +566 -0
- data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
- data/redesign/config_design.rb +501 -0
- metadata +19 -5
- data/lib/git/base.rb +0 -1204
- data/lib/git/lib.rb +0 -2855
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30866d1c683ae76cdb07eb526d9adecd45c8ec08bb53824c3a027984297ec256
|
|
4
|
+
data.tar.gz: a326ea01ed821d33c4edc60963cd2f6473ecaae2cad0daa82c06747456daa8dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
413
|
-
|
|
428
|
+
def remote_add(name, url, opts = {})
|
|
429
|
+
facade_repository.remote_add(name, url, opts)
|
|
414
430
|
end
|
|
415
431
|
```
|
|
416
432
|
|
|
417
|
-
(
|
|
418
|
-
|
|
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::
|
|
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::
|
|
91
|
-
`Git::
|
|
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.
|
|
420
|
-
r = repo.
|
|
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.
|
|
441
|
-
repo.
|
|
442
|
-
repo.
|
|
443
|
-
repo.
|
|
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.
|
|
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
|
|
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'
|