git 5.0.2 → 5.0.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/.claude/commands/address-copilot-reviews.md +14 -0
- data/.claude/settings.json +16 -0
- data/.claude/skills +1 -0
- data/.github/copilot-instructions.md +0 -12
- data/.github/hooks/run-bin-setup-once.sh +9 -3
- data/.github/pull_request_template.md +2 -0
- data/.github/skills/command-implementation/REFERENCE.md +1 -1
- data/.github/skills/command-implementation/SKILL.md +2 -2
- data/.github/skills/facade-implementation/SKILL.md +2 -2
- data/.github/skills/rspec-unit-testing-standards/SKILL.md +52 -6
- data/.github/workflows/continuous_integration.yml +12 -0
- data/.gitignore +3 -0
- data/.release-please-manifest.json +1 -1
- data/.rspec +0 -1
- data/CHANGELOG.md +41 -0
- data/CLAUDE.md +11 -0
- data/CONTRIBUTING.md +149 -0
- data/Rakefile +2 -13
- data/git.gemspec +17 -5
- data/lib/git/commands/arguments.rb +2 -2
- data/lib/git/commands/remote/remove.rb +3 -5
- data/lib/git/commands/remote/rename.rb +7 -2
- data/lib/git/parsers/diff.rb +2 -3
- data/lib/git/parsers/grep.rb +1 -1
- data/lib/git/version.rb +1 -1
- data/tasks/rspec.rake +93 -30
- metadata +11 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6dcf907e2c0ccb6d916a9aa1d43f6c768e49555465a484e0231d919cb889b889
|
|
4
|
+
data.tar.gz: 5cce608675a41031f79318065e8c11125f9f23a154ab2ae6f578192010b04367
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42dbbce6613ecd406e64771bb03d85416a4eae0fe12183fee03640292f5f18700175788c35c55836ad2d572bde6857421ed6cae8f9d7520dcd8bd12b498ae809
|
|
7
|
+
data.tar.gz: d55dd4bd23e700aacd23d8b850adada2e0e3a8f90176515e5de3b5213e43282ac5e8e105d500243bc54a0655472368e00becf4b6c01e59e4baf842223f8f89a7
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Address all unresolved Copilot review threads on the active pull request
|
|
3
|
+
argument-hint: "[PR number — defaults to the PR for the current branch]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Follow the shared agent prompt in
|
|
7
|
+
`.github/prompts/iteratively-address-copilot-reviews.prompt.md` exactly. That file
|
|
8
|
+
is the canonical instructions for this task and is shared with other AI agents; read
|
|
9
|
+
it before doing anything else.
|
|
10
|
+
|
|
11
|
+
@.github/prompts/iteratively-address-copilot-reviews.prompt.md
|
|
12
|
+
|
|
13
|
+
If `$ARGUMENTS` is non-empty, treat it as the pull request number to operate on
|
|
14
|
+
instead of detecting the PR from the current branch.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "\"$CLAUDE_PROJECT_DIR/.github/hooks/run-bin-setup-once.sh\"",
|
|
10
|
+
"timeout": 900
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
data/.claude/skills
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../.github/skills
|
|
@@ -22,18 +22,6 @@ fine to use `#` in footer values such as `Closes: #999` or `Refs: #999`.
|
|
|
22
22
|
- **RuboCop:** Use `RuboCop` for prose and `rubocop` for CLI or gem names. Never use
|
|
23
23
|
`Rubocop`.
|
|
24
24
|
|
|
25
|
-
## Project Commands
|
|
26
|
-
|
|
27
|
-
| Purpose | Command |
|
|
28
|
-
| --- | --- |
|
|
29
|
-
| First-time project or new worktree setup | `bin/setup` |
|
|
30
|
-
| Run all tests and linters (CI-equivalent) | `bundle exec rake` |
|
|
31
|
-
| Run all RSpec tests | `bundle exec rake spec` |
|
|
32
|
-
| Run RSpec unit tests | `bundle exec rake spec:unit` |
|
|
33
|
-
| Run RSpec integration tests | `bundle exec rake spec:integration` |
|
|
34
|
-
| Run a specific RSpec spec | `bundle exec rspec <path>` |
|
|
35
|
-
| Run linters | `bundle exec rake rubocop yard build` |
|
|
36
|
-
|
|
37
25
|
## Branch & PR Strategy
|
|
38
26
|
|
|
39
27
|
| Target | When |
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
+
# This hook is shared by every agent configured for this repo (GitHub Copilot via
|
|
5
|
+
# .github/hooks/, Claude Code via .claude/settings.json), so do not assume the
|
|
6
|
+
# caller's working directory.
|
|
7
|
+
command -v git >/dev/null 2>&1 || exit 0
|
|
8
|
+
cd "$(dirname "${BASH_SOURCE[0]}")/../.." || exit 0
|
|
9
|
+
|
|
4
10
|
# Skip if this repo does not use bin/setup.
|
|
5
11
|
[[ -x "./bin/setup" ]] || exit 0
|
|
6
12
|
|
|
7
|
-
# Keep setup idempotent per-worktree.
|
|
8
|
-
|
|
9
|
-
marker="$(git rev-parse --git-path
|
|
13
|
+
# Keep setup idempotent per-worktree. The marker is shared across agents so that
|
|
14
|
+
# cloning and opening the repo in two different tools does not run setup twice.
|
|
15
|
+
marker="$(git rev-parse --git-path agent-bin-setup.done 2>/dev/null)" || exit 0
|
|
10
16
|
[[ -f "$marker" ]] && exit 0
|
|
11
17
|
|
|
12
18
|
./bin/setup
|
|
@@ -16,4 +16,6 @@ Review our [guidelines for contributing](https://github.com/ruby-git/ruby-git/bl
|
|
|
16
16
|
- [ ] I ran `bundle exec rake` locally on this branch and it passed (see
|
|
17
17
|
[Local development setup](../CONTRIBUTING.md#local-development-setup)).
|
|
18
18
|
- [ ] Tests added/updated as needed.
|
|
19
|
+
- [ ] `bundle exec rake spec:unit` reports 100% line and branch coverage (see
|
|
20
|
+
[Test coverage policy](../CONTRIBUTING.md#test-coverage-policy)).
|
|
19
21
|
- [ ] Documentation updated where applicable (README and/or YARD).
|
|
@@ -908,7 +908,7 @@ For migration PRs, verify process constraints:
|
|
|
908
908
|
- each slice is independently revertible
|
|
909
909
|
- refactor-only changes are not mixed with unrelated behavior changes
|
|
910
910
|
- quality gates pass for the slice — discover tasks via
|
|
911
|
-
`bundle exec ruby -e "require 'rake'; load 'Rakefile'; puts Rake::Task['default
|
|
911
|
+
`bundle exec ruby -e "require 'rake'; load 'Rakefile'; puts Rake::Task['default'].prerequisites"`
|
|
912
912
|
and run each individually via `bundle exec rake <task>`, fixing failures before
|
|
913
913
|
advancing
|
|
914
914
|
|
|
@@ -188,11 +188,11 @@ This skill supports three modes. Determine which mode applies before starting:
|
|
|
188
188
|
requirements](REFERENCE.md#phased-rollout-requirements).
|
|
189
189
|
|
|
190
190
|
5. **Run quality gates** — discover the prerequisite tasks for
|
|
191
|
-
`default
|
|
191
|
+
`default` and run them sequentially, fixing failures before
|
|
192
192
|
advancing:
|
|
193
193
|
|
|
194
194
|
```bash
|
|
195
|
-
bundle exec ruby -e "require 'rake'; load 'Rakefile'; puts Rake::Task['default
|
|
195
|
+
bundle exec ruby -e "require 'rake'; load 'Rakefile'; puts Rake::Task['default'].prerequisites"
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
Run each listed task **individually** in order via `bundle exec rake <task>`
|
|
@@ -224,11 +224,11 @@ This skill supports three modes. Determine which mode applies before starting:
|
|
|
224
224
|
- has tests that verify call-shape compatibility when classification is
|
|
225
225
|
`legacy-contract` (positional hash and/or keyword-arg / `**opts` forms where required)
|
|
226
226
|
|
|
227
|
-
5. **Run quality gates** — discover the prerequisite tasks for `default
|
|
227
|
+
5. **Run quality gates** — discover the prerequisite tasks for `default`
|
|
228
228
|
and run them sequentially, fixing failures before advancing:
|
|
229
229
|
|
|
230
230
|
```bash
|
|
231
|
-
bundle exec ruby -e "require 'rake'; load 'Rakefile'; puts Rake::Task['default
|
|
231
|
+
bundle exec ruby -e "require 'rake'; load 'Rakefile'; puts Rake::Task['default'].prerequisites"
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
Run each listed task **individually** in order via `bundle exec rake <task>`
|
|
@@ -68,9 +68,13 @@ Adoption and enforcement notes:
|
|
|
68
68
|
- Apply these rules as hard requirements for new and modified unit specs.
|
|
69
69
|
- Legacy specs may violate some rules; treat those as incremental cleanup work.
|
|
70
70
|
- Branch and line coverage are both reported by SimpleCov in this repository.
|
|
71
|
-
- `
|
|
72
|
-
`
|
|
73
|
-
|
|
71
|
+
- `minimum_coverage: { line: 100, branch: 100 }` is configured and enforced: a full
|
|
72
|
+
`rake spec:unit` run fails when either threshold is missed, and CI fails the pull
|
|
73
|
+
request with it. Rule 21 is a build gate, not just a review check. See the
|
|
74
|
+
[Test coverage policy](../../../CONTRIBUTING.md#test-coverage-policy) in
|
|
75
|
+
`CONTRIBUTING.md` for the full policy.
|
|
76
|
+
- Focused runs (`SPEC=<glob> rake spec:unit`, or `rspec <file>`) report coverage but
|
|
77
|
+
do not fail on it, since they load all of `lib/` while exercising only a slice.
|
|
74
78
|
|
|
75
79
|
## Related skills
|
|
76
80
|
|
|
@@ -484,10 +488,50 @@ Every conditional path through the public interface must be exercised by at leas
|
|
|
484
488
|
one example. If a branch cannot be reached through the public interface, that is a
|
|
485
489
|
design smell.
|
|
486
490
|
|
|
491
|
+
This is enforced by the build: a full `rake spec:unit` run fails below 100% line or
|
|
492
|
+
branch coverage, and the failure output names each uncovered line and branch.
|
|
493
|
+
|
|
494
|
+
When a branch is hard to cover, apply these in order:
|
|
495
|
+
|
|
496
|
+
1. **Reach it through the public interface** — if it is reachable, test it.
|
|
497
|
+
2. **Delete it** — a branch unreachable through the public interface is usually dead
|
|
498
|
+
code. Removing it is preferable to excluding it.
|
|
499
|
+
3. **Exclude it with `# simplecov:disable`** — the last resort, per the exception below.
|
|
500
|
+
|
|
487
501
|
> **Exception:** Defensive guards that require breaking OS-level invariants to reach
|
|
488
502
|
> (e.g., `raise "unreachable"` that would require triggering out-of-memory) may be
|
|
489
|
-
> excluded. Mark them explicitly with `# :
|
|
490
|
-
> — never leave branches silently uncovered.
|
|
503
|
+
> excluded. Mark them explicitly with a `# simplecov:disable` directive carrying a
|
|
504
|
+
> reason — never leave branches silently uncovered. Cover the smallest possible span,
|
|
505
|
+
> and expect a reviewer to challenge it. `lib/` currently contains no coverage
|
|
506
|
+
> directives.
|
|
507
|
+
|
|
508
|
+
Prefer the inline form for a single line. It applies only to its own line and needs no
|
|
509
|
+
matching `enable`, so a region can never be left open by accident:
|
|
510
|
+
|
|
511
|
+
```ruby
|
|
512
|
+
raise 'unreachable' # simplecov:disable defensive guard; only reachable on OOM
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Use the block form only for a genuine span, and close it explicitly:
|
|
516
|
+
|
|
517
|
+
```ruby
|
|
518
|
+
# simplecov:disable branch platform-specific fallback; not reachable on MRI
|
|
519
|
+
...
|
|
520
|
+
# simplecov:enable branch
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Always name the narrowest criterion that solves the problem — `line`, `branch`,
|
|
524
|
+
`method`, or a comma-separated combination — and write the reason after it, so the
|
|
525
|
+
required justification lives in the directive.
|
|
526
|
+
|
|
527
|
+
> **Review check:** verify the criterion is spelled exactly. A word SimpleCov does not
|
|
528
|
+
> recognize is parsed as free-form reason text, which silently widens the directive to
|
|
529
|
+
> all three criteria rather than failing.
|
|
530
|
+
|
|
531
|
+
> **Coverage is a floor on evidence, not a proof of correctness.** Never write an
|
|
532
|
+
> example whose only purpose is to execute a line. Such a test turns the report green
|
|
533
|
+
> while violating [Rule 24](#rule-24-must-assert-observable-behavior-not-implementation-details),
|
|
534
|
+
> and must be rejected in review.
|
|
491
535
|
|
|
492
536
|
### Rule 22 (MUST): Error assertions must specify both the error class and a message pattern
|
|
493
537
|
|
|
@@ -614,7 +658,9 @@ After writing or modifying tests, verify compliance before finishing:
|
|
|
614
658
|
|
|
615
659
|
1. **Run the specs:** `bundle exec rspec spec/unit/path/to_spec.rb`
|
|
616
660
|
2. **Check branch coverage** meets 100% (Rule 21) — open `coverage/index.html` and
|
|
617
|
-
confirm no uncovered branches in the class under test.
|
|
661
|
+
confirm no uncovered lines or branches in the class under test. A focused run
|
|
662
|
+
reports coverage but does not fail on it; `bundle exec rake spec:unit` is the
|
|
663
|
+
authority and will fail below 100%, naming each uncovered line and branch.
|
|
618
664
|
3. **Re-check MUST rules.** Scan the spec against every MUST rule. Fix violations.
|
|
619
665
|
4. **Run in random order** (Rule 27): `bundle exec rspec spec/unit/path/to_spec.rb --order rand`
|
|
620
666
|
|
|
@@ -53,5 +53,17 @@ jobs:
|
|
|
53
53
|
run: bundle exec rake default
|
|
54
54
|
timeout-minutes: 15
|
|
55
55
|
|
|
56
|
+
# The unit suite enforces 100% line and branch coverage (see the "Test coverage
|
|
57
|
+
# policy" section of CONTRIBUTING.md). The failure log names the uncovered lines
|
|
58
|
+
# and branches; this artifact adds the browsable HTML report for the same run.
|
|
59
|
+
- name: Upload Coverage Report
|
|
60
|
+
if: failure()
|
|
61
|
+
uses: actions/upload-artifact@v7
|
|
62
|
+
with:
|
|
63
|
+
name: coverage-ruby-${{ matrix.ruby }}-${{ matrix.operating-system }}
|
|
64
|
+
path: coverage/
|
|
65
|
+
if-no-files-found: ignore
|
|
66
|
+
retention-days: 7
|
|
67
|
+
|
|
56
68
|
- name: Test Gem
|
|
57
69
|
run: bundle exec rake test:gem
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,47 @@
|
|
|
5
5
|
|
|
6
6
|
# Change Log
|
|
7
7
|
|
|
8
|
+
## [5.0.3](https://github.com/ruby-git/ruby-git/compare/v5.0.2...v5.0.3) (2026-08-07)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Drop unsupported -- separator from remote remove/rename commands ([8ec466f](https://github.com/ruby-git/ruby-git/commit/8ec466f8b282a5e94abd7d499bc26a92d94b2fc3))
|
|
14
|
+
* Remove unreachable case branch in Patch#apply_file_mode ([896fc49](https://github.com/ruby-git/ruby-git/commit/896fc49e3c2c03c401fe1b213c3f75bb40730521))
|
|
15
|
+
* Remove unreachable nil check in Git::Parsers::Grep.parse ([74e919a](https://github.com/ruby-git/ruby-git/commit/74e919a401126ad4415615f92cc9597e966239c6))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Other Changes
|
|
19
|
+
|
|
20
|
+
* Add Claude Code support alongside GitHub Copilot ([37f7d6a](https://github.com/ruby-git/ruby-git/commit/37f7d6a14c6d53bcf5b9644b8023d323290a5977))
|
|
21
|
+
* Add unit spec for Git::EncodingUtils ([5f0f10e](https://github.com/ruby-git/ruby-git/commit/5f0f10e4db78324bbe453de49598564a25f3428a))
|
|
22
|
+
* Consolidate parallel and serial spec rake tasks ([49616b1](https://github.com/ruby-git/ruby-git/commit/49616b12f52c6941a8617afbf7c56e7d7d5fe0f6))
|
|
23
|
+
* Cover :index option in Git::Factories#worktree_open_options_after_init ([efeba22](https://github.com/ruby-git/ruby-git/commit/efeba2294bb05327489a6523c46f26dc7392a84e))
|
|
24
|
+
* Cover #path when both src and dst are nil ([3a1a05a](https://github.com/ruby-git/ruby-git/commit/3a1a05a9be13057a1fc304df5008866634179124))
|
|
25
|
+
* Cover edge-case branches in Git::Parsers::Diff ([38d8b3f](https://github.com/ruby-git/ruby-git/commit/38d8b3f88f4f76a65776e9f6b8b87c6bb839e710))
|
|
26
|
+
* Cover Errno::ESRCH timeout-race rescue in run_process_executer ([899bf36](https://github.com/ruby-git/ruby-git/commit/899bf36399489038524cecf1d7ac2c606df03bde))
|
|
27
|
+
* Cover Git::Branch#merge ([71c0ea3](https://github.com/ruby-git/ruby-git/commit/71c0ea39d4c786a38245a8fb95fd93235ee03903))
|
|
28
|
+
* Cover Git::Branch#stashes ([27ba130](https://github.com/ruby-git/ruby-git/commit/27ba130a6cd3ef78171d1bb20230459c3f678a3a))
|
|
29
|
+
* Cover Git::Diff#name_status ([fa2ee98](https://github.com/ruby-git/ruby-git/commit/fa2ee98fd4613177843d285f7bc4a2ffb44ca79c))
|
|
30
|
+
* Cover Git::Factories#parse_clone_stderr unparseable-output branch ([65aa9b9](https://github.com/ruby-git/ruby-git/commit/65aa9b9318c7f8a8986c2aaae7dd0af0aeec4d69))
|
|
31
|
+
* Cover Git::Parsers::CatFile.each_header without trailing separator ([b7a9ed9](https://github.com/ruby-git/ruby-git/commit/b7a9ed93e03dcbd22a4fb25e9bb7b9c49647262d))
|
|
32
|
+
* Cover Git::Parsers::LsTree.parse with no tab-separated filename ([98b9ebf](https://github.com/ruby-git/ruby-git/commit/98b9ebf03c49f51c76b2e56dd670a9f68f220da8))
|
|
33
|
+
* Cover Git::Parsers::Remote.apply_pair unrecognized-variable branch ([03b10f6](https://github.com/ruby-git/ruby-git/commit/03b10f618a2f012b578ab995120cedbcd96ed8d1))
|
|
34
|
+
* Cover Git::Stashes#save ([7bca04c](https://github.com/ruby-git/ruby-git/commit/7bca04c53e64c747627784773d0c03ad0b9ff2e3))
|
|
35
|
+
* Cover Git.const_missing fallback branch for unrelated constants ([05c3c43](https://github.com/ruby-git/ruby-git/commit/05c3c4350e18bf1403fa314902708cfc40848c70))
|
|
36
|
+
* Cover Git.export ([4bd7f58](https://github.com/ruby-git/ruby-git/commit/4bd7f58432a19385ff55aecca9e89e2d9a96fa4a))
|
|
37
|
+
* Cover normalize_fetch_keys with a String option key ([47fb7fe](https://github.com/ruby-git/ruby-git/commit/47fb7fe061df9177a26ec7ada90b35b902ffc1df))
|
|
38
|
+
* Disable coverage for spec:integration, add SPEC filtering, fix formatters ([cb53d03](https://github.com/ruby-git/ruby-git/commit/cb53d03f8b671873f24368aadcafec638de32345))
|
|
39
|
+
* Document the project test coverage policy ([d378c1e](https://github.com/ruby-git/ruby-git/commit/d378c1e8b75e0c4209e17cc040ed36eb93053797))
|
|
40
|
+
* Enforce 100% unit test coverage on pull requests ([a446098](https://github.com/ruby-git/ruby-git/commit/a446098af548039b2c8b68bbb6562a53cb79fe40))
|
|
41
|
+
* Guard checkout-index nonexistent-path spec for git < 2.30.0 ([cf212a9](https://github.com/ruby-git/ruby-git/commit/cf212a98b41218a7940ce54aa9b7c56a630412c3))
|
|
42
|
+
* Guard worktree repair spec for git < 2.29.0 ([c527fb1](https://github.com/ruby-git/ruby-git/commit/c527fb15c2f21f9be5913192d43c847cf49681eb))
|
|
43
|
+
* **merge:** Fix incorrect version-gated failure expectation in quit_spec ([24c5ba4](https://github.com/ruby-git/ruby-git/commit/24c5ba4ce77b02413485d226a94b283f6af9d110))
|
|
44
|
+
* Read the gem version without loading lib/git/version.rb ([45c4b14](https://github.com/ruby-git/ruby-git/commit/45c4b14e944fcb43bda800823e377cf954ce2d3b))
|
|
45
|
+
* Require git 2.39.0 for maintenance register/unregister --config-file specs ([5b1fd52](https://github.com/ruby-git/ruby-git/commit/5b1fd527c39e4b3f1796851795a44694d94f4402))
|
|
46
|
+
* Skip get-urlmatch scope spec on git < 2.42.0 ([e1ede7a](https://github.com/ruby-git/ruby-git/commit/e1ede7adddea28b654a183c618fc2336b5ffb83b))
|
|
47
|
+
* Upgrade simplecov and simplecov-rspec to ~> 1.0 ([65e92b9](https://github.com/ruby-git/ruby-git/commit/65e92b9a9a621435efbf2a0855d48c47bb62425d))
|
|
48
|
+
|
|
8
49
|
## [5.0.2](https://github.com/ruby-git/ruby-git/compare/v5.0.1...v5.0.2) (2026-08-02)
|
|
9
50
|
|
|
10
51
|
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file exists so Claude Code picks up the same project instructions as every
|
|
4
|
+
other AI coding agent used in this repository.
|
|
5
|
+
|
|
6
|
+
**Do not add project guidance here.** The canonical instructions live in
|
|
7
|
+
[`.github/copilot-instructions.md`](.github/copilot-instructions.md) and are
|
|
8
|
+
imported below. Edit that file so all agents stay in sync. Reserve this file for
|
|
9
|
+
guidance that applies *only* to Claude Code.
|
|
10
|
+
|
|
11
|
+
@.github/copilot-instructions.md
|
data/CONTRIBUTING.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- [Before requesting review](#before-requesting-review)
|
|
21
21
|
- [Branch strategy](#branch-strategy)
|
|
22
22
|
- [AI-assisted contributions](#ai-assisted-contributions)
|
|
23
|
+
- [Agent configuration](#agent-configuration)
|
|
23
24
|
- [Agent skills](#agent-skills)
|
|
24
25
|
- [Design philosophy](#design-philosophy)
|
|
25
26
|
- [Layered architecture](#layered-architecture)
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
- [What to know about Conventional Commits](#what-to-know-about-conventional-commits)
|
|
40
41
|
- [Issue and PR references](#issue-and-pr-references)
|
|
41
42
|
- [Testing guidelines](#testing-guidelines)
|
|
43
|
+
- [Test coverage policy](#test-coverage-policy)
|
|
42
44
|
- [Unit tests vs Integration tests](#unit-tests-vs-integration-tests)
|
|
43
45
|
- [Building a specific version of the Git command-line](#building-a-specific-version-of-the-git-command-line)
|
|
44
46
|
- [Install pre-requisites](#install-pre-requisites)
|
|
@@ -215,6 +217,27 @@ and "CI is green" are not substitutes for the submitter running
|
|
|
215
217
|
[the local validation step](#contributor-validation-policy) themselves; CI is a
|
|
216
218
|
backstop, not a primary validation surface.
|
|
217
219
|
|
|
220
|
+
### Agent configuration
|
|
221
|
+
|
|
222
|
+
Agent configuration is shared: each piece of guidance is stored once and surfaced to
|
|
223
|
+
every supported agent.
|
|
224
|
+
|
|
225
|
+
| Content | Canonical location | Also read by |
|
|
226
|
+
| --- | --- | --- |
|
|
227
|
+
| Project instructions | [`.github/copilot-instructions.md`](.github/copilot-instructions.md) | Claude Code, via an import in [`CLAUDE.md`](CLAUDE.md) |
|
|
228
|
+
| Skills | [`.github/skills/`](.github/skills/) | Claude Code, via the `.claude/skills` symlink |
|
|
229
|
+
| Prompts | [`.github/prompts/`](.github/prompts/) | Claude Code, via wrappers in `.claude/commands/` |
|
|
230
|
+
| Setup hook | [`.github/hooks/run-bin-setup-once.sh`](.github/hooks/run-bin-setup-once.sh) | Claude Code, via `.claude/settings.json` |
|
|
231
|
+
|
|
232
|
+
Always edit the canonical file. The Claude Code side is a pointer in every case, so
|
|
233
|
+
changes reach both agents without a sync step.
|
|
234
|
+
|
|
235
|
+
One caveat: `.claude/skills` is a committed symlink. Git for Windows only
|
|
236
|
+
materializes symlinks when `core.symlinks` is enabled (which requires Developer Mode
|
|
237
|
+
or an elevated shell). Without it, Windows contributors get a plain text file there
|
|
238
|
+
and Claude Code silently loads no skills; either enable symlinks or point your agent
|
|
239
|
+
at [`.github/skills/`](.github/skills/) directly. Copilot is unaffected.
|
|
240
|
+
|
|
218
241
|
### Agent skills
|
|
219
242
|
|
|
220
243
|
If you use an AI coding agent that understands repository skills, the
|
|
@@ -904,12 +927,138 @@ process.stdin.on('end', () =>
|
|
|
904
927
|
local working copy.
|
|
905
928
|
- Test runs are covered by SimpleCov by default. Set `COVERAGE=false` (or `0`/`no`/
|
|
906
929
|
`off`) to skip coverage, e.g. `COVERAGE=false bundle exec rake spec`.
|
|
930
|
+
`rake spec:integration` always disables coverage, regardless of `COVERAGE`:
|
|
931
|
+
integration tests aren't meant to be exhaustive, so tracking their coverage would
|
|
932
|
+
misleadingly suggest that low integration coverage is a problem to fix.
|
|
933
|
+
- `rake spec:integration` runs in parallel (via `parallel_tests`) on MRI. Set
|
|
934
|
+
`PARALLEL_TESTS=false` (or `0`/`no`/`off`) to force serial execution, e.g.
|
|
935
|
+
`PARALLEL_TESTS=false bundle exec rake spec:integration`. A run narrowed by `SPEC`
|
|
936
|
+
to a single spec file always runs serially — there is nothing to divide across
|
|
937
|
+
workers, and serial execution gives per-example (documentation) output.
|
|
938
|
+
- Set `SPEC=<glob>` to run specific files instead of a task's whole directory, e.g.
|
|
939
|
+
`SPEC=spec/unit/git/version_spec.rb bundle exec rake spec:unit`.
|
|
940
|
+
- Each task runs only the matches that live under its own directory, so one glob
|
|
941
|
+
spanning `spec/unit/` and `spec/integration/` can drive `bundle exec rake spec` and
|
|
942
|
+
exercise both layers of an area in a single command:
|
|
943
|
+
|
|
944
|
+
```bash
|
|
945
|
+
# Unit and integration specs for the add command
|
|
946
|
+
SPEC=spec/**/git/commands/add_spec.rb bundle exec rake spec
|
|
947
|
+
|
|
948
|
+
# Unit and integration specs for every command class
|
|
949
|
+
SPEC=spec/**/git/commands bundle exec rake spec
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
The glob is expanded by Rake, not the shell, so `**` works the same in any shell.
|
|
953
|
+
A task whose directory contains none of the matches is skipped with a message —
|
|
954
|
+
`SPEC=spec/unit/...` on `rake spec` runs the unit specs and skips
|
|
955
|
+
`spec:integration`. A glob matching nothing anywhere fails the task outright.
|
|
907
956
|
|
|
908
957
|
This project uses **RSpec** (`spec/`) as its sole test framework. Structure,
|
|
909
958
|
naming, setup, stubbing, and coverage rules for unit specs are defined in the
|
|
910
959
|
[`rspec-unit-testing-standards`](.github/skills/rspec-unit-testing-standards/SKILL.md)
|
|
911
960
|
skill — follow it when writing or reviewing specs under `spec/unit/`.
|
|
912
961
|
|
|
962
|
+
#### Test coverage policy
|
|
963
|
+
|
|
964
|
+
**Every pull request to `main` must keep `bundle exec rake spec:unit` at 100% line
|
|
965
|
+
coverage and 100% branch coverage of `lib/`.** CI fails the build when it drops
|
|
966
|
+
below either threshold.
|
|
967
|
+
|
|
968
|
+
This is enforceable without being onerous because unit coverage in this project is
|
|
969
|
+
deterministic: `lib/` has no Ruby-version, Ruby-engine, or platform conditionals, and
|
|
970
|
+
no unit spec is conditionally skipped. Every supported MRI runtime measures exactly
|
|
971
|
+
the same lines and branches, so a coverage failure is always something the pull
|
|
972
|
+
request introduced.
|
|
973
|
+
|
|
974
|
+
What the policy does and does not cover:
|
|
975
|
+
|
|
976
|
+
- **Scope is the unit suite on MRI.** Integration specs are deliberately not
|
|
977
|
+
exhaustive and are not measured (`rake spec:integration` always disables coverage).
|
|
978
|
+
JRuby and TruffleRuby do not produce reliable coverage data and are not measured.
|
|
979
|
+
- **Enforcement applies to full-suite runs.** A focused run
|
|
980
|
+
(`SPEC=<glob> bundle exec rake spec:unit`, or `bundle exec rspec <file>`) still
|
|
981
|
+
reports coverage but will not fail on it, so the usual edit-test loop is unaffected.
|
|
982
|
+
Set `FAIL_ON_LOW_COVERAGE=true` to force enforcement on for a focused run.
|
|
983
|
+
- **A focused run lists gaps only in the code it is about.** The reported percentage is
|
|
984
|
+
always for the whole of `lib/`, but the list of uncovered lines and branches is scoped
|
|
985
|
+
to the files the run tests — the classes it describes, plus the `lib/` file each spec
|
|
986
|
+
file mirrors. So a focused run answers "is what I just changed fully covered?" without
|
|
987
|
+
waiting for CI:
|
|
988
|
+
|
|
989
|
+
```text
|
|
990
|
+
Reporting uncovered lines and branches for 1 of 225 files.
|
|
991
|
+
|
|
992
|
+
No uncovered lines and branches in this file.
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
Ignore the percentage on a focused run; it is low because `spec_helper` loads all of
|
|
996
|
+
`lib/`, not because anything is wrong. Set `LIST_UNCOVERED_FILES=all` to list gaps for
|
|
997
|
+
every file instead.
|
|
998
|
+
- **The thresholds do not move.** Do not lower `minimum_coverage` and do not add a
|
|
999
|
+
SimpleCov filter to exclude a file. The only sanctioned escape hatch is a
|
|
1000
|
+
`# simplecov:disable` directive.
|
|
1001
|
+
- **Every file under `lib/` is measured.** SimpleCov only tracks files loaded after it
|
|
1002
|
+
starts, so a file loaded earlier is silently absent from the report rather than
|
|
1003
|
+
counted as uncovered. This is why `git.gemspec` reads the version string out of
|
|
1004
|
+
`lib/git/version.rb` instead of `require`ing it: the `Gemfile` uses `gemspec`, so
|
|
1005
|
+
requiring it there would load it on every `bundle exec`, before SimpleCov starts.
|
|
1006
|
+
Keep new code out of the load path that runs ahead of `spec_helper`.
|
|
1007
|
+
|
|
1008
|
+
When a branch is hard to cover, apply these in order:
|
|
1009
|
+
|
|
1010
|
+
1. **Reach it through the public interface.** If a branch is reachable, test it.
|
|
1011
|
+
2. **Delete it.** A branch that cannot be reached through the public interface is
|
|
1012
|
+
usually dead code, and removing it is preferable to excluding it. See commit
|
|
1013
|
+
`74e919a4` ("fix: remove unreachable nil check in `Git::Parsers::Grep.parse`") for
|
|
1014
|
+
the pattern.
|
|
1015
|
+
3. **Exclude it with `# simplecov:disable`.** Reserved for defensive guards that could
|
|
1016
|
+
only be reached by breaking an OS-level invariant. Cover the smallest possible span,
|
|
1017
|
+
state why the code is unreachable, and expect a reviewer to question it. `lib/`
|
|
1018
|
+
currently contains no coverage directives.
|
|
1019
|
+
|
|
1020
|
+
Use the inline form wherever the exclusion is a single line — it applies only to the
|
|
1021
|
+
line it sits on and needs no matching `enable`, which makes it impossible to leave a
|
|
1022
|
+
region accidentally open:
|
|
1023
|
+
|
|
1024
|
+
```ruby
|
|
1025
|
+
raise 'unreachable' # simplecov:disable defensive guard; only reachable on OOM
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
The block form covers a span and stays in effect until the matching
|
|
1029
|
+
`# simplecov:enable` (or end of file, if you forget it):
|
|
1030
|
+
|
|
1031
|
+
```ruby
|
|
1032
|
+
# simplecov:disable branch platform-specific fallback; not reachable on MRI
|
|
1033
|
+
...
|
|
1034
|
+
# simplecov:enable branch
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
Always name the narrowest criterion that solves the problem — `line`, `branch`,
|
|
1038
|
+
`method`, or a comma-separated combination — and spell it exactly. A word SimpleCov
|
|
1039
|
+
does not recognize is treated as free-form reason text, which silently widens the
|
|
1040
|
+
directive to all three criteria instead of failing. Write the reason after the
|
|
1041
|
+
criteria, so the required justification lives in the directive itself.
|
|
1042
|
+
|
|
1043
|
+
**Coverage is a floor on evidence, not a proof of correctness.** A test written only
|
|
1044
|
+
to execute a line, without asserting a meaningful outcome, violates Rule 24 of the
|
|
1045
|
+
[`rspec-unit-testing-standards`](.github/skills/rspec-unit-testing-standards/SKILL.md)
|
|
1046
|
+
skill and will be rejected in review even though it turns the report green. The
|
|
1047
|
+
threshold exists to surface untested behavior, not to be satisfied.
|
|
1048
|
+
|
|
1049
|
+
To see exactly what is uncovered:
|
|
1050
|
+
|
|
1051
|
+
```bash
|
|
1052
|
+
# Names every uncovered line and branch (printed automatically when a full run fails)
|
|
1053
|
+
$ LIST_UNCOVERED_DETAIL=true bundle exec rake spec:unit
|
|
1054
|
+
|
|
1055
|
+
# Browsable HTML report, also uploaded as a CI artifact when a build fails
|
|
1056
|
+
$ open coverage/index.html
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
This policy applies to `main` only. The `4.x` maintenance branch predates it and is
|
|
1060
|
+
not held to these thresholds.
|
|
1061
|
+
|
|
913
1062
|
#### Unit tests vs Integration tests
|
|
914
1063
|
|
|
915
1064
|
This project uses two types of RSpec tests, organized by directory:
|
data/Rakefile
CHANGED
|
@@ -9,19 +9,8 @@ default_tasks = %i[spec:unit spec:integration rubocop]
|
|
|
9
9
|
default_tasks << :yard if Rake::Task.task_defined?(:yard)
|
|
10
10
|
default_tasks << :build
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
default_tasks_parallel << :build
|
|
15
|
-
|
|
16
|
-
# Use parallel test execution for MRI where it cuts build times by 30-48%.
|
|
17
|
-
# JRuby and TruffleRuby are slower with parallel_tests because each worker
|
|
18
|
-
# process pays JVM/Truffle startup and warm-up overhead independently,
|
|
19
|
-
# resulting in 18-28% slower builds vs. serial execution.
|
|
20
|
-
parallel_supported = RUBY_ENGINE == 'ruby'
|
|
21
|
-
task default: parallel_supported ? default_tasks_parallel : default_tasks
|
|
22
|
-
|
|
23
|
-
desc 'Same as default but runs tests in parallel'
|
|
24
|
-
task 'default:parallel': default_tasks_parallel
|
|
12
|
+
desc 'Run all CI tasks (tests, linters, yard, and build)'
|
|
13
|
+
task default: default_tasks
|
|
25
14
|
|
|
26
15
|
module Rake
|
|
27
16
|
# Overload Rake::Task to add logging
|
data/git.gemspec
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
# Read the version out of lib/git/version.rb rather than requiring it.
|
|
4
|
+
#
|
|
5
|
+
# The Gemfile uses `gemspec`, so Bundler evaluates this file on every `bundle exec`.
|
|
6
|
+
# `require`ing lib/git/version.rb here would therefore load it before SimpleCov
|
|
7
|
+
# starts in spec_helper, leaving it invisible to the coverage report -- an
|
|
8
|
+
# unmeasured hole in the 100% coverage gate. See the "Test coverage policy" section
|
|
9
|
+
# of CONTRIBUTING.md.
|
|
10
|
+
#
|
|
11
|
+
# release-please rewrites the `VERSION = '...'` assignment in place (see
|
|
12
|
+
# `version-file` in .release-please-config.json), so matching on it stays correct
|
|
13
|
+
# across releases. If the match ever fails, the raise below stops the build
|
|
14
|
+
# immediately rather than letting a nil version reach the specification.
|
|
15
|
+
version = File.read(File.expand_path('lib/git/version.rb', __dir__))[/^\s*VERSION\s*=\s*['"]([^'"]+)['"]/, 1]
|
|
16
|
+
raise 'Could not determine Git::VERSION from lib/git/version.rb' if version.nil?
|
|
5
17
|
|
|
6
18
|
Gem::Specification.new do |spec|
|
|
7
19
|
spec.author = 'Scott Chacon and others'
|
|
@@ -17,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
|
17
29
|
including branching and merging, object inspection and manipulation, history, patch
|
|
18
30
|
generation and more.
|
|
19
31
|
DESCRIPTION
|
|
20
|
-
spec.version =
|
|
32
|
+
spec.version = version
|
|
21
33
|
|
|
22
34
|
spec.metadata['homepage_uri'] = spec.homepage
|
|
23
35
|
spec.metadata['source_code_uri'] = spec.homepage
|
|
@@ -41,9 +53,9 @@ Gem::Specification.new do |spec|
|
|
|
41
53
|
spec.add_development_dependency 'rake', '~> 13.3'
|
|
42
54
|
spec.add_development_dependency 'rspec', '~> 3.13'
|
|
43
55
|
spec.add_development_dependency 'rubocop', '~> 1.82'
|
|
44
|
-
spec.add_development_dependency 'simplecov', '~> 0
|
|
56
|
+
spec.add_development_dependency 'simplecov', '~> 1.0'
|
|
45
57
|
spec.add_development_dependency 'simplecov-lcov', '~> 0.9'
|
|
46
|
-
spec.add_development_dependency 'simplecov-rspec', '~>
|
|
58
|
+
spec.add_development_dependency 'simplecov-rspec', '~> 1.1'
|
|
47
59
|
|
|
48
60
|
if RUBY_ENGINE == 'truffleruby' && Gem::Version.new(RUBY_ENGINE_VERSION) < Gem::Version.new('34.0.0')
|
|
49
61
|
# i18n 1.15+ uses Fiber.[] (Ruby 3.2 Fiber storage) which TruffleRuby < 34.0.0 does not implement
|
|
@@ -2399,11 +2399,11 @@ module Git
|
|
|
2399
2399
|
build_option(args, entry[:name], @option_definitions[entry[:name]], normalized_opts[entry[:name]])
|
|
2400
2400
|
when :operand
|
|
2401
2401
|
build_single_positional(args, entry[:name], allocated_positionals)
|
|
2402
|
-
# :
|
|
2402
|
+
# simplecov:disable this case should be unreachable
|
|
2403
2403
|
else
|
|
2404
2404
|
raise ArgumentError, "unknown entry kind: #{entry[:kind].inspect}"
|
|
2405
2405
|
end
|
|
2406
|
-
# :
|
|
2406
|
+
# simplecov:enable
|
|
2407
2407
|
end
|
|
2408
2408
|
|
|
2409
2409
|
# Replace the END_OF_OPTIONS_MARKER with the stored `as:` value if any element
|
|
@@ -13,9 +13,9 @@ module Git
|
|
|
13
13
|
# remove = Git::Commands::Remote::Remove.new(execution_context)
|
|
14
14
|
# remove.call('origin')
|
|
15
15
|
#
|
|
16
|
-
# @
|
|
17
|
-
#
|
|
18
|
-
#
|
|
16
|
+
# @note No `end_of_options` boundary: `git remote remove` did not accept a `--`
|
|
17
|
+
# separator until git 2.38.0, so a name that looks like a flag (e.g. `-weird`)
|
|
18
|
+
# raises `ArgumentError` instead of being sent to git.
|
|
19
19
|
#
|
|
20
20
|
# @note `arguments` block audited against https://git-scm.com/docs/git-remote/2.53.0
|
|
21
21
|
#
|
|
@@ -30,8 +30,6 @@ module Git
|
|
|
30
30
|
literal 'remote'
|
|
31
31
|
literal 'remove'
|
|
32
32
|
|
|
33
|
-
end_of_options
|
|
34
|
-
|
|
35
33
|
operand :name, required: true
|
|
36
34
|
end
|
|
37
35
|
|
|
@@ -22,6 +22,13 @@ module Git
|
|
|
22
22
|
# rename = Git::Commands::Remote::Rename.new(execution_context)
|
|
23
23
|
# rename.call('origin', 'upstream', no_progress: true)
|
|
24
24
|
#
|
|
25
|
+
# @note No `end_of_options` boundary despite `:progress` preceding the operands:
|
|
26
|
+
# `git remote rename` did not accept a `--` separator until git 2.36.0. Since
|
|
27
|
+
# the `old`/`new` operands are still validated (an operand is rejected as
|
|
28
|
+
# option-like whenever no `--` boundary exists anywhere in the definition),
|
|
29
|
+
# a name that looks like a flag (e.g. `-weird`) raises `ArgumentError` instead
|
|
30
|
+
# of ever reaching git, so omitting the boundary introduces no ambiguity.
|
|
31
|
+
#
|
|
25
32
|
# @note `arguments` block audited against https://git-scm.com/docs/git-remote/2.53.0
|
|
26
33
|
#
|
|
27
34
|
# @see Git::Commands::Remote
|
|
@@ -36,8 +43,6 @@ module Git
|
|
|
36
43
|
literal 'rename'
|
|
37
44
|
flag_option :progress, negatable: true
|
|
38
45
|
|
|
39
|
-
end_of_options
|
|
40
|
-
|
|
41
46
|
operand :old, required: true
|
|
42
47
|
operand :new, required: true
|
|
43
48
|
end
|
data/lib/git/parsers/diff.rb
CHANGED
|
@@ -593,11 +593,10 @@ module Git
|
|
|
593
593
|
# @return [void]
|
|
594
594
|
#
|
|
595
595
|
def apply_file_mode(type, mode)
|
|
596
|
-
|
|
597
|
-
when 'new'
|
|
596
|
+
if type == 'new'
|
|
598
597
|
@current_file[:dst_mode] = mode
|
|
599
598
|
@current_file[:src_path] = nil
|
|
600
|
-
|
|
599
|
+
else
|
|
601
600
|
@current_file[:src_mode] = mode
|
|
602
601
|
@current_file[:dst_path] = nil
|
|
603
602
|
end
|
data/lib/git/parsers/grep.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Git
|
|
|
32
32
|
def parse(output)
|
|
33
33
|
output.each_line.with_object(Hash.new { |h, k| h[k] = [] }) do |line, hsh|
|
|
34
34
|
filename, line_num, text = line.chomp.split("\0", 3)
|
|
35
|
-
next unless text && line_num
|
|
35
|
+
next unless text && line_num.match?(/\A\d+\z/)
|
|
36
36
|
|
|
37
37
|
hsh[filename] << [line_num.to_i, text]
|
|
38
38
|
end
|
data/lib/git/version.rb
CHANGED
data/tasks/rspec.rake
CHANGED
|
@@ -1,48 +1,111 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Skip parallel test execution when the PARALLEL_TESTS environment variable is set to a falsy value
|
|
4
|
+
def parallel_tests_disabled_via_env?
|
|
5
|
+
parallel_tests_env = ENV.fetch('PARALLEL_TESTS', 'true').strip.downcase
|
|
6
|
+
%w[false 0 no off].include?(parallel_tests_env)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Use parallel test execution for MRI where it cuts build times by 30-48%.
|
|
10
|
+
# JRuby and TruffleRuby are slower with parallel_tests because each worker
|
|
11
|
+
# process pays JVM/Truffle startup and warm-up overhead independently,
|
|
12
|
+
# resulting in 18-28% slower builds vs. serial execution.
|
|
13
|
+
def parallel_tests?
|
|
14
|
+
RUBY_ENGINE == 'ruby' && !parallel_tests_disabled_via_env?
|
|
15
|
+
end
|
|
4
16
|
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
# SPEC lets a developer target one or more specific files, e.g.
|
|
18
|
+
# `SPEC=spec/unit/git/version_spec.rb rake spec:unit`, running just those files
|
|
19
|
+
# instead of the whole directory. Both rspec and parallel_rspec recurse a bare
|
|
20
|
+
# directory argument using their own default *_spec.rb pattern, so no explicit
|
|
21
|
+
# --pattern flag is needed for the non-SPEC case.
|
|
22
|
+
#
|
|
23
|
+
# Matches are filtered to dir so that each task only ever runs the specs it owns,
|
|
24
|
+
# no matter how broad SPEC is. This keeps every task self-consistent (a unit spec
|
|
25
|
+
# can never be run by spec:integration under integration's runner and env) and lets
|
|
26
|
+
# a single cross-layer glob drive `rake spec`, e.g.
|
|
27
|
+
# `SPEC=spec/**/git/commands/add_spec.rb rake spec` runs the unit file under
|
|
28
|
+
# spec:unit and the integration file under spec:integration. A SPEC that matches
|
|
29
|
+
# files elsewhere in spec/ but none under dir yields no targets, and the caller
|
|
30
|
+
# skips the task rather than falling back to running all of dir.
|
|
31
|
+
def spec_targets(dir)
|
|
32
|
+
spec_files = ENV.fetch('SPEC', nil)&.strip
|
|
33
|
+
return [dir] if spec_files.nil? || spec_files.empty?
|
|
34
|
+
|
|
35
|
+
targets = FileList[spec_files].sort
|
|
36
|
+
raise "SPEC=#{spec_files.inspect} did not match any files" if targets.empty?
|
|
37
|
+
|
|
38
|
+
targets.select { |target| target.delete_prefix('./').start_with?(dir) }
|
|
8
39
|
end
|
|
9
40
|
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# it runs. When the suite hangs, the last printed line identifies the blocking
|
|
15
|
-
# test. Mirrors the same diagnostic added to spec:integration in PR #1274.
|
|
16
|
-
t.rspec_opts = '--format documentation' if RUBY_ENGINE == 'jruby'
|
|
41
|
+
# True when targets name exactly one spec file (as opposed to a directory, or
|
|
42
|
+
# several files).
|
|
43
|
+
def single_spec_file?(targets)
|
|
44
|
+
targets.one? && File.file?(targets.first)
|
|
17
45
|
end
|
|
18
46
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
# The runner for targets: parallel_rspec only when the task runs in parallel and
|
|
48
|
+
# there is more than one spec file to spread across workers.
|
|
49
|
+
#
|
|
50
|
+
# A single file always uses plain rspec. Worker startup is pure overhead with nothing
|
|
51
|
+
# to divide, and parallel_tests sets TEST_ENV_NUMBER in every worker, which makes
|
|
52
|
+
# spec_helper fall back from the documentation formatter to plain dots -- the opposite
|
|
53
|
+
# of what a run narrowed to one file wants.
|
|
54
|
+
def spec_runner(targets, parallel:)
|
|
55
|
+
parallel && !single_spec_file?(targets) ? 'parallel_rspec' : 'rspec'
|
|
26
56
|
end
|
|
27
57
|
|
|
28
|
-
# Run
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
# Run the SPEC-filtered targets for dir, or announce a skip when SPEC selected nothing
|
|
59
|
+
# under dir. Silently doing nothing would read as a passing run of the whole directory.
|
|
60
|
+
def run_spec_task(name, dir, env, parallel:)
|
|
61
|
+
targets = spec_targets(dir)
|
|
62
|
+
|
|
63
|
+
if targets.empty?
|
|
64
|
+
puts "Skipping #{name}: SPEC=#{ENV.fetch('SPEC', nil)} matched no files under #{dir}"
|
|
65
|
+
else
|
|
66
|
+
sh env, 'bundle', 'exec', spec_runner(targets, parallel: parallel), *targets
|
|
67
|
+
end
|
|
32
68
|
end
|
|
33
69
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
def define_parallel_spec_task(name, dir, env: {})
|
|
71
|
+
task name do
|
|
72
|
+
run_spec_task(name, dir, env, parallel: true)
|
|
73
|
+
end
|
|
38
74
|
end
|
|
39
75
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
76
|
+
def define_serial_spec_task(name, dir, env: {})
|
|
77
|
+
task name do
|
|
78
|
+
run_spec_task(name, dir, env, parallel: false)
|
|
79
|
+
end
|
|
44
80
|
end
|
|
45
81
|
|
|
82
|
+
# Define a spec task that runs in parallel (via parallel_tests) when parallel_tests? is
|
|
83
|
+
# true, and serially (via a direct `bundle exec rspec` call) otherwise. env, when given,
|
|
84
|
+
# is passed to `sh` and scoped to just the spawned test subprocess(es).
|
|
85
|
+
def define_spec_task(name, dir, desc_text, env: {})
|
|
86
|
+
desc desc_text
|
|
87
|
+
parallel_tests? ? define_parallel_spec_task(name, dir, env: env) : define_serial_spec_task(name, dir, env: env)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Run only unit specs. These run in a few seconds, so parallel_tests startup
|
|
91
|
+
# overhead isn't worth it - always run serially.
|
|
92
|
+
desc 'Run unit RSpec tests (mocked, fast; SPEC=<glob> to run specific files)'
|
|
93
|
+
define_serial_spec_task('spec:unit', 'spec/unit/')
|
|
94
|
+
|
|
95
|
+
# Run only integration specs. Coverage is forced off (regardless of the COVERAGE
|
|
96
|
+
# env var): integration tests intentionally aren't exhaustive, so tracking or
|
|
97
|
+
# reporting coverage for them would misleadingly suggest that low integration
|
|
98
|
+
# coverage is a problem to fix.
|
|
99
|
+
define_spec_task(
|
|
100
|
+
'spec:integration', 'spec/integration/',
|
|
101
|
+
'Run integration RSpec tests (real git, slower; SPEC=<glob> to run specific files)',
|
|
102
|
+
env: { 'COVERAGE' => 'false' }
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Run all specs, keeping unit and integration output clearly separated
|
|
106
|
+
desc 'Run unit and integration RSpec tests (SPEC=<glob> to run specific files)'
|
|
107
|
+
task spec: %w[spec:unit spec:integration]
|
|
108
|
+
|
|
46
109
|
CLEAN << 'coverage'
|
|
47
110
|
CLEAN << '.rspec_status'
|
|
48
111
|
CLEAN << 'rspec-report.xml'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Chacon and others
|
|
@@ -169,14 +169,14 @@ dependencies:
|
|
|
169
169
|
requirements:
|
|
170
170
|
- - "~>"
|
|
171
171
|
- !ruby/object:Gem::Version
|
|
172
|
-
version: '0
|
|
172
|
+
version: '1.0'
|
|
173
173
|
type: :development
|
|
174
174
|
prerelease: false
|
|
175
175
|
version_requirements: !ruby/object:Gem::Requirement
|
|
176
176
|
requirements:
|
|
177
177
|
- - "~>"
|
|
178
178
|
- !ruby/object:Gem::Version
|
|
179
|
-
version: '0
|
|
179
|
+
version: '1.0'
|
|
180
180
|
- !ruby/object:Gem::Dependency
|
|
181
181
|
name: simplecov-lcov
|
|
182
182
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -197,14 +197,14 @@ dependencies:
|
|
|
197
197
|
requirements:
|
|
198
198
|
- - "~>"
|
|
199
199
|
- !ruby/object:Gem::Version
|
|
200
|
-
version: '
|
|
200
|
+
version: '1.1'
|
|
201
201
|
type: :development
|
|
202
202
|
prerelease: false
|
|
203
203
|
version_requirements: !ruby/object:Gem::Requirement
|
|
204
204
|
requirements:
|
|
205
205
|
- - "~>"
|
|
206
206
|
- !ruby/object:Gem::Version
|
|
207
|
-
version: '
|
|
207
|
+
version: '1.1'
|
|
208
208
|
- !ruby/object:Gem::Dependency
|
|
209
209
|
name: irb
|
|
210
210
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -298,6 +298,9 @@ executables: []
|
|
|
298
298
|
extensions: []
|
|
299
299
|
extra_rdoc_files: []
|
|
300
300
|
files:
|
|
301
|
+
- ".claude/commands/address-copilot-reviews.md"
|
|
302
|
+
- ".claude/settings.json"
|
|
303
|
+
- ".claude/skills"
|
|
301
304
|
- ".commitlintrc.yml"
|
|
302
305
|
- ".dockerignore"
|
|
303
306
|
- ".github/copilot-instructions.md"
|
|
@@ -356,6 +359,7 @@ files:
|
|
|
356
359
|
- ".yardopts"
|
|
357
360
|
- AI_POLICY.md
|
|
358
361
|
- CHANGELOG.md
|
|
362
|
+
- CLAUDE.md
|
|
359
363
|
- CODE_OF_CONDUCT.md
|
|
360
364
|
- CONTRIBUTING.md
|
|
361
365
|
- GOVERNANCE.md
|
|
@@ -626,8 +630,8 @@ licenses:
|
|
|
626
630
|
metadata:
|
|
627
631
|
homepage_uri: http://github.com/ruby-git/ruby-git
|
|
628
632
|
source_code_uri: http://github.com/ruby-git/ruby-git
|
|
629
|
-
changelog_uri: https://rubydoc.info/gems/git/5.0.
|
|
630
|
-
documentation_uri: https://rubydoc.info/gems/git/5.0.
|
|
633
|
+
changelog_uri: https://rubydoc.info/gems/git/5.0.3/file/CHANGELOG.md
|
|
634
|
+
documentation_uri: https://rubydoc.info/gems/git/5.0.3
|
|
631
635
|
rubygems_mfa_required: 'true'
|
|
632
636
|
rdoc_options: []
|
|
633
637
|
require_paths:
|