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.
- 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 +90 -22
- 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/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 +288 -128
- data/lib/git/stash.rb +2 -9
- data/lib/git/stashes.rb +2 -7
- data/lib/git/status.rb +12 -23
- 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 +182 -77
- data/redesign/3_architecture_implementation.md +170 -112
- data/redesign/Phase 4 - Step A.md +366 -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 -6
- data/lib/git/base.rb +0 -1204
- data/lib/git/lib.rb +0 -2855
- data/lib/git/repository/configuring.rb +0 -156
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '085739aa78a2d5bfacbf1f2048fb15997b8021ea40fda26715e9a66f7d2d8178'
|
|
4
|
+
data.tar.gz: f83f025bc403549cb4f4f889f2ed83ee2af9afdc2947a535345970c47ffa87b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2769eab52af46ddc54bfea220b51f5b450e6f3e9999288f3b7d2003940dc4bf428a3b682c34ed41b36294d297ecda67abb48fa680063c396afbd1e45cbd71b75
|
|
7
|
+
data.tar.gz: 81aaa29f87a49c7cf1573e3fc1bbc32e9eb9883952c7550b69f983b73ebf33264f700d0465bb1ac390390083b8becdaefe80faebe3200adcd4d815c895cad57e
|
|
@@ -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,8 @@ 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-26: v5.0.0.beta.3 Released](#2026-06-26-v500beta3-released)
|
|
34
|
+
- [2026-06-25: v5.0.0.beta.2 Released](#2026-06-25-v500beta2-released)
|
|
33
35
|
- [2026-06-04: v5.0.0.beta.1 Released](#2026-06-04-v500beta1-released)
|
|
34
36
|
- [2026-01-07: AI Policy Introduced](#2026-01-07-ai-policy-introduced)
|
|
35
37
|
- [2025-07-09: Architectural Redesign](#2025-07-09-architectural-redesign)
|
|
@@ -52,7 +54,7 @@ Get started by obtaining a repository object by:
|
|
|
52
54
|
[Git.clone](https://rubydoc.info/gems/git/Git#clone-class_method)
|
|
53
55
|
|
|
54
56
|
Methods that can be called on a repository object are documented in
|
|
55
|
-
[Git::
|
|
57
|
+
[Git::Repository](https://rubydoc.info/gems/git/Git/Repository)
|
|
56
58
|
|
|
57
59
|
## Install
|
|
58
60
|
|
|
@@ -87,8 +89,8 @@ All functionality for this gem starts with the top-level
|
|
|
87
89
|
non-repo scoped `git` commands such as `config`.
|
|
88
90
|
|
|
89
91
|
The `Git` module also has factory methods such as `open`, `clone`, and `init` which
|
|
90
|
-
return a [`Git::
|
|
91
|
-
`Git::
|
|
92
|
+
return a [`Git::Repository`](https://rubydoc.info/gems/git/Git/Repository) object. The
|
|
93
|
+
`Git::Repository` object is used to run repo-specific `git` commands such as `add`,
|
|
92
94
|
`commit`, `push`, and `log`.
|
|
93
95
|
|
|
94
96
|
Clone, read status, and log:
|
|
@@ -133,15 +135,17 @@ Configure the `git` command line:
|
|
|
133
135
|
|
|
134
136
|
```ruby
|
|
135
137
|
# Global config (in ~/.gitconfig)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
entries = Git.config_list(global: true) # returns Array<Git::ConfigEntryInfo>
|
|
139
|
+
entry = Git.config_get('user.email', global: true) # returns Git::ConfigEntryInfo or nil
|
|
140
|
+
email = entry&.value # => "user@example.com" or nil
|
|
141
|
+
Git.config_set('user.email', 'user@example.com', global: true)
|
|
139
142
|
|
|
140
143
|
# Repository config
|
|
141
144
|
repo = Git.open('path/to/repo')
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
entries = repo.config_list # returns Array<Git::ConfigEntryInfo>
|
|
146
|
+
entry = repo.config_get('user.email') # returns Git::ConfigEntryInfo or nil
|
|
147
|
+
email = entry&.value # => "anotheruser@example.com" or nil
|
|
148
|
+
repo.config_set('user.email', 'anotheruser@example.com')
|
|
145
149
|
```
|
|
146
150
|
|
|
147
151
|
Configure the git gem:
|
|
@@ -292,8 +296,8 @@ result = repo.fsck(unreachable: true, strict: true)
|
|
|
292
296
|
# Suppress dangling object output
|
|
293
297
|
result = repo.fsck(dangling: false)
|
|
294
298
|
|
|
295
|
-
repo.
|
|
296
|
-
repo.
|
|
299
|
+
repo.config_get('user.name')&.value # returns 'Scott Chacon'
|
|
300
|
+
repo.config_list # returns Array<Git::ConfigEntryInfo>
|
|
297
301
|
|
|
298
302
|
# Configuration can be set when cloning using the :config option.
|
|
299
303
|
# This option can be an single configuration String or an Array
|
|
@@ -342,8 +346,8 @@ path = '/tmp/clone'
|
|
|
342
346
|
repo = Git.clone(git_url, name, :path => path)
|
|
343
347
|
repo.dir #=> /tmp/clone/ruby-git-clean
|
|
344
348
|
|
|
345
|
-
repo.
|
|
346
|
-
repo.
|
|
349
|
+
repo.config_set('user.name', 'Scott Chacon')
|
|
350
|
+
repo.config_set('user.email', 'email@email.com')
|
|
347
351
|
|
|
348
352
|
# Clone can take a filter to tell the serve to send a partial clone
|
|
349
353
|
repo = Git.clone(git_url, name, :path => path, :filter => 'tree:0')
|
|
@@ -370,7 +374,7 @@ repo.commit('message')
|
|
|
370
374
|
repo.commit_all('message')
|
|
371
375
|
|
|
372
376
|
# Sign a commit using the gpg key configured in the user.signingkey config setting
|
|
373
|
-
repo.
|
|
377
|
+
repo.config_set('user.signingkey', '0A46826A')
|
|
374
378
|
repo.commit('message', gpg_sign: true)
|
|
375
379
|
|
|
376
380
|
# Sign a commit using a specified gpg key
|
|
@@ -416,8 +420,8 @@ repo.merge([branch1, branch2])
|
|
|
416
420
|
|
|
417
421
|
repo.merge_base('branch1', 'branch2')
|
|
418
422
|
|
|
419
|
-
r = repo.
|
|
420
|
-
r = repo.
|
|
423
|
+
r = repo.remote_add(name, uri) # Git::Remote
|
|
424
|
+
r = repo.remote_add(name, other_repo) # Git::Remote (other_repo is a Git::Repository instance)
|
|
421
425
|
|
|
422
426
|
repo.remotes # array of Git::Remotes
|
|
423
427
|
repo.remote(name).fetch
|
|
@@ -437,12 +441,12 @@ repo.fetch('origin', {:'update-head-ok' => true})
|
|
|
437
441
|
repo.pull
|
|
438
442
|
repo.pull(Git::Repo, Git::Branch) # fetch and a merge
|
|
439
443
|
|
|
440
|
-
repo.
|
|
441
|
-
repo.
|
|
442
|
-
repo.
|
|
443
|
-
repo.
|
|
444
|
+
repo.tag_add('tag_name') # returns Git::Object::Tag
|
|
445
|
+
repo.tag_add('tag_name', 'object_reference')
|
|
446
|
+
repo.tag_add('tag_name', 'object_reference', {:options => 'here'})
|
|
447
|
+
repo.tag_add('tag_name', {:options => 'here'})
|
|
444
448
|
|
|
445
|
-
repo.
|
|
449
|
+
repo.tag_delete('tag_name')
|
|
446
450
|
|
|
447
451
|
repo.repack
|
|
448
452
|
|
|
@@ -650,17 +654,81 @@ notes.
|
|
|
650
654
|
|
|
651
655
|
## 📢 Project Announcements 📢
|
|
652
656
|
|
|
657
|
+
### 2026-06-26: v5.0.0.beta.3 Released
|
|
658
|
+
|
|
659
|
+
The architectural redesign is approximately **93% complete** and we have published
|
|
660
|
+
[`git v5.0.0.beta.3`](https://rubygems.org/gems/git/versions/5.0.0.beta.3) as our
|
|
661
|
+
third pre-release.
|
|
662
|
+
|
|
663
|
+
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
664
|
+
|
|
665
|
+
```ruby
|
|
666
|
+
gem 'git', '~> 5.0.0.beta'
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Or install it directly:
|
|
670
|
+
|
|
671
|
+
```sh
|
|
672
|
+
gem install git --pre
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
The intent is full backward compatibility with v4.x, but given the size and scope of
|
|
676
|
+
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
677
|
+
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
678
|
+
unexpected — your feedback helps us ship a solid v5.0.0.
|
|
679
|
+
|
|
680
|
+
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
681
|
+
|
|
682
|
+
### 2026-06-25: v5.0.0.beta.2 Released
|
|
683
|
+
|
|
684
|
+
The architectural redesign is approximately **90% complete** and we have published
|
|
685
|
+
[`git v5.0.0.beta.2`](https://rubygems.org/gems/git/versions/5.0.0.beta.2) as our
|
|
686
|
+
second pre-release.
|
|
687
|
+
|
|
688
|
+
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
689
|
+
|
|
690
|
+
```ruby
|
|
691
|
+
gem 'git', '~> 5.0.0.beta'
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Or install it directly:
|
|
695
|
+
|
|
696
|
+
```sh
|
|
697
|
+
gem install git --pre
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
The intent is full backward compatibility with v4.x, but given the size and scope of
|
|
701
|
+
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
702
|
+
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
703
|
+
unexpected — your feedback helps us ship a solid v5.0.0.
|
|
704
|
+
|
|
705
|
+
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
706
|
+
|
|
653
707
|
### 2026-06-04: v5.0.0.beta.1 Released
|
|
654
708
|
|
|
655
709
|
The architectural redesign is approximately **65% complete** and we have published
|
|
656
|
-
[`git
|
|
710
|
+
[`git v5.0.0.beta.1`](https://rubygems.org/gems/git/versions/5.0.0.beta.1) as our
|
|
657
711
|
first pre-release.
|
|
658
712
|
|
|
713
|
+
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
714
|
+
|
|
715
|
+
```ruby
|
|
716
|
+
gem 'git', '~> 5.0.0.beta'
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Or install it directly:
|
|
720
|
+
|
|
721
|
+
```sh
|
|
722
|
+
gem install git --pre
|
|
723
|
+
```
|
|
724
|
+
|
|
659
725
|
The intent is full backward compatibility with 4.x, but given the size and scope of
|
|
660
726
|
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
661
727
|
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
662
728
|
unexpected — your feedback helps us ship a solid 5.0.0.
|
|
663
729
|
|
|
730
|
+
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
731
|
+
|
|
664
732
|
### 2026-01-07: AI Policy Introduced
|
|
665
733
|
|
|
666
734
|
We have adopted a formal [AI Policy](AI_POLICY.md) to clarify expectations for
|