git 5.2.0 → 5.3.0
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/CHANGELOG.md +33 -0
- data/CONTRIBUTING.md +17 -4
- data/README.md +48 -7
- data/UPGRADING.md +287 -1
- data/lib/git/author.rb +11 -0
- data/lib/git/author_info.rb +66 -0
- data/lib/git/branch.rb +210 -15
- data/lib/git/branch_info.rb +1 -1
- data/lib/git/branches.rb +35 -7
- data/lib/git/commands/cat_file/raw.rb +60 -6
- data/lib/git/object.rb +13 -7
- data/lib/git/parsers/stash.rb +50 -17
- data/lib/git/parsers/tag.rb +54 -8
- data/lib/git/remote.rb +37 -7
- data/lib/git/remote_info.rb +67 -10
- data/lib/git/repository/branching.rb +111 -1
- data/lib/git/repository/merging.rb +96 -2
- data/lib/git/repository/remote_operations.rb +57 -0
- data/lib/git/repository/shared_private.rb +67 -0
- data/lib/git/stash_info.rb +32 -34
- data/lib/git/tag_info.rb +21 -29
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aedbda16896f72f7adc9320f2e1ade684cd1f4a49a392929ce3eb64d0320ca31
|
|
4
|
+
data.tar.gz: 8a77684970cde47e1ce927b2c4dab8c1e3c7b9356d876bbcb66a79c5d39769f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37f051a10d8fad9eb51e3bebd2cefd84d523ca5961bb1fabc619f01a2db28f9c271a2a97701e8a575d2ab2e3244c5c1ee36b97bf9b94f45caa29f2f4a46340c1
|
|
7
|
+
data.tar.gz: 8c9cb46f8a7a74eff2488ef24e3641a3f6dccadee4735009290450630f3aab6bc90402e333c45c5061d8cb276767971eb316cba00196a1b7c3f3a6d7ad4fda85
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,39 @@
|
|
|
5
5
|
|
|
6
6
|
# Change Log
|
|
7
7
|
|
|
8
|
+
## [5.3.0](https://github.com/ruby-git/ruby-git/compare/v5.2.0...v5.3.0) (2026-09-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **author:** Add immutable Git::AuthorInfo and deprecate Git::Author ([c08b04b](https://github.com/ruby-git/ruby-git/commit/c08b04bf25142140c626fd16354355aae26b4fba)), closes [#1721](https://github.com/ruby-git/ruby-git/issues/1721)
|
|
14
|
+
* **branch:** Deprecate Git::Branch and Git::Branches in favor of Git::Repository#branch_list ([1581aa0](https://github.com/ruby-git/ruby-git/commit/1581aa00b3fc7984a2024b290b83f6f6f5e97838)), closes [#1639](https://github.com/ruby-git/ruby-git/issues/1639)
|
|
15
|
+
* **branch:** Deprecate Git::Branch#stashes in favor of Git::Repository#stashes_all ([dcee889](https://github.com/ruby-git/ruby-git/commit/dcee889a63bfd38cc3073577b55e2112721c5e33)), closes [#1637](https://github.com/ruby-git/ruby-git/issues/1637)
|
|
16
|
+
* **cat-file:** Deprecate the allow_unknown_type option of Git::Commands::CatFile::Raw ([95769ea](https://github.com/ruby-git/ruby-git/commit/95769ea683349c445a7a7ea7b839fc990d2d8c4b)), closes [#1709](https://github.com/ruby-git/ruby-git/issues/1709)
|
|
17
|
+
* **info:** Nest Git::AuthorInfo in Git::TagInfo and Git::StashInfo ([2f44f96](https://github.com/ruby-git/ruby-git/commit/2f44f9617122c3719625cf3a56f4b263b0de4c3c)), closes [#1745](https://github.com/ruby-git/ruby-git/issues/1745)
|
|
18
|
+
* **remote:** Deprecate Git::Remote and Git::Repository#remote in favor of #remote_list ([1abb585](https://github.com/ruby-git/ruby-git/commit/1abb585339c0d003622719aa5930b5bde2262502)), closes [#1643](https://github.com/ruby-git/ruby-git/issues/1643)
|
|
19
|
+
* **remote:** Freeze Git::RemoteInfo array-valued members ([4d44a3d](https://github.com/ruby-git/ruby-git/commit/4d44a3dc8fed77b39c7146675eaa9d46eb12e34d))
|
|
20
|
+
* **repository:** Add in_branch and merge_into facade methods ([59b58d0](https://github.com/ruby-git/ruby-git/commit/59b58d0389656051e8cd61bd856dc116139c2507)), closes [#1641](https://github.com/ruby-git/ruby-git/issues/1641)
|
|
21
|
+
* **repository:** Deprecate Git::Repository#remotes in favor of #remote_list ([410095c](https://github.com/ruby-git/ruby-git/commit/410095c4d2a3065c222f9cc750a05143230801d7)), closes [#1640](https://github.com/ruby-git/ruby-git/issues/1640)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **branching:** Verify refs/heads when classifying the HEAD state ([bfc4d8a](https://github.com/ruby-git/ruby-git/commit/bfc4d8a2b82000906d1a398c69125fcfdc22c276)), closes [#1641](https://github.com/ruby-git/ruby-git/issues/1641)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Other Changes
|
|
30
|
+
|
|
31
|
+
* **adr:** Supersede ADR-0006 with a release-based deprecation period ([b3b32e3](https://github.com/ruby-git/ruby-git/commit/b3b32e339f3804d022ea83cec03977039ecdcace))
|
|
32
|
+
* **author:** Add failing tests for Git::AuthorInfo and Git::Author deprecation ([e0d3123](https://github.com/ruby-git/ruby-git/commit/e0d3123fadd836e301160f492e0a1303f43267b8)), closes [#1721](https://github.com/ruby-git/ruby-git/issues/1721)
|
|
33
|
+
* **branch:** Add failing test for the Git::Branch#stashes deprecation ([a806808](https://github.com/ruby-git/ruby-git/commit/a806808f3e5a5a38a92646c1ab64c8906b3dc525))
|
|
34
|
+
* **contributing:** Describe the release line and maintenance branch rules ([fd78c9c](https://github.com/ruby-git/ruby-git/commit/fd78c9ccd262470c9e33166ba63dc095b2060a5d))
|
|
35
|
+
* **info:** Add failing tests for nested Git::AuthorInfo on TagInfo and StashInfo ([a7f5e69](https://github.com/ruby-git/ruby-git/commit/a7f5e6988beaa85bce68e6295ad23a2754338d5b)), closes [#1745](https://github.com/ruby-git/ruby-git/issues/1745)
|
|
36
|
+
* **readme:** Add the deprecation and release support policies ([50ac2e2](https://github.com/ruby-git/ruby-git/commit/50ac2e28bdd6ddb9349b8980e457441a0d39f6c9))
|
|
37
|
+
* **remote:** Add failing tests for the Git::Remote and Git::Repository#remote deprecations ([36af404](https://github.com/ruby-git/ruby-git/commit/36af40419603f9c0a03f424e3839a1d7337b517b))
|
|
38
|
+
* **skills:** State the deprecation policy and the major release checklists ([a246cf8](https://github.com/ruby-git/ruby-git/commit/a246cf8fee93a96132f2bd1b0796899204b830a1))
|
|
39
|
+
* **upgrading:** Add the v6.0.0 upgrade procedure ([4d7c58c](https://github.com/ruby-git/ruby-git/commit/4d7c58c7a0e963da6146608093e9373dade1efe4))
|
|
40
|
+
|
|
8
41
|
## [5.2.0](https://github.com/ruby-git/ruby-git/compare/v5.1.0...v5.2.0) (2026-08-30)
|
|
9
42
|
|
|
10
43
|
|
data/CONTRIBUTING.md
CHANGED
|
@@ -221,10 +221,14 @@ first keeps the review cycle short.
|
|
|
221
221
|
|
|
222
222
|
This project maintains two active branches:
|
|
223
223
|
|
|
224
|
-
- **`main`**:
|
|
225
|
-
|
|
226
|
-
- **`4.x`**:
|
|
227
|
-
fixes and backward-compatible
|
|
224
|
+
- **`main`**: All development. It releases the next version of the gem, including
|
|
225
|
+
the next major version.
|
|
226
|
+
- **`4.x`**: The maintenance branch for the most recent previous major series. It
|
|
227
|
+
receives bug fixes and security fixes, and backward-compatible features at the
|
|
228
|
+
maintainers' discretion.
|
|
229
|
+
|
|
230
|
+
The README's [Release support policy](README.md#release-support-policy) says how long
|
|
231
|
+
each major series is supported.
|
|
228
232
|
|
|
229
233
|
When submitting a pull request:
|
|
230
234
|
|
|
@@ -232,6 +236,15 @@ When submitting a pull request:
|
|
|
232
236
|
- **Bug fixes**: Target `main`, and maintainers will backport to `4.x` if applicable
|
|
233
237
|
- **Security fixes**: Target both branches or `4.x` if the issue only affects v4.x
|
|
234
238
|
|
|
239
|
+
Removing a deprecated API follows the
|
|
240
|
+
[deprecation policy](.github/skills/breaking-change-analysis/SKILL.md#step-4-deprecation-policy):
|
|
241
|
+
|
|
242
|
+
A removal PR merges to main only when its deprecation warning and `UPGRADING.md` entry
|
|
243
|
+
are contained in a previous normal release. Once any removal has merged to main, main
|
|
244
|
+
becomes the release line for the next major version. If another release of the
|
|
245
|
+
previous major is needed, it is cut from a branch created for that major (e.g. `4.x`
|
|
246
|
+
or `5.x`).
|
|
247
|
+
|
|
235
248
|
## AI-assisted contributions
|
|
236
249
|
|
|
237
250
|
AI-assisted contributions are welcome. Please review and apply our [AI
|
data/README.md
CHANGED
|
@@ -35,6 +35,8 @@ Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?log
|
|
|
35
35
|
- [Project policies](#project-policies)
|
|
36
36
|
- [Ruby version support policy](#ruby-version-support-policy)
|
|
37
37
|
- [Git version support policy](#git-version-support-policy)
|
|
38
|
+
- [Deprecation policy](#deprecation-policy)
|
|
39
|
+
- [Release support policy](#release-support-policy)
|
|
38
40
|
- [Project announcements](#project-announcements)
|
|
39
41
|
- [2026-08-23: v5.x deprecations and the v6.0.0 roadmap](#2026-08-23-v5x-deprecations-and-the-v600-roadmap)
|
|
40
42
|
- [2026-07-28: v5.0.0 released](#2026-07-28-v500-released)
|
|
@@ -296,9 +298,9 @@ See [the Active Support Deprecation
|
|
|
296
298
|
documentation](https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html)
|
|
297
299
|
for more details.
|
|
298
300
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
301
|
+
Before upgrading the git gem to the next major version, follow the upgrade procedure
|
|
302
|
+
in [UPGRADING.md](UPGRADING.md#upgrading-to-v600). It turns the warnings into errors so
|
|
303
|
+
that you cannot miss one.
|
|
302
304
|
|
|
303
305
|
For the full list of deprecated methods and their replacements, see
|
|
304
306
|
[UPGRADING.md](UPGRADING.md).
|
|
@@ -382,6 +384,8 @@ opening issues or pull requests.
|
|
|
382
384
|
| [AI_POLICY](AI_POLICY.md) | AI-assisted contributions are welcome. Contributors are expected to read and apply the AI Policy, and ensure any AI-assisted work meets our quality, security, and licensing standards. |
|
|
383
385
|
| [Ruby version support policy](#ruby-version-support-policy) | Supported Ruby runtimes and platforms; bump decisions and CI coverage expectations. |
|
|
384
386
|
| [Git version support policy](#git-version-support-policy) | Minimum supported git version and how version bumps are communicated and enforced. |
|
|
387
|
+
| [Deprecation policy](#deprecation-policy) | When an API may be removed, what a deprecation warning says, and how to upgrade across a major version. |
|
|
388
|
+
| [Release support policy](#release-support-policy) | Which branch releases what, and how long each major series is supported. |
|
|
385
389
|
| [GOVERNANCE](GOVERNANCE.md) | Principles-first governance defining maintainer/project lead roles, least-privilege access, consensus/majority decisions, and nomination/emeritus steps. |
|
|
386
390
|
| [MAINTAINERS](MAINTAINERS.md) | Lists active maintainers (Project Lead noted) and emeritus alumni with links; see governance for role scope. |
|
|
387
391
|
| [LICENSE](LICENSE) | MIT License terms for using, modifying, and redistributing this project. |
|
|
@@ -418,6 +422,42 @@ The supported git version may be increased in future major or minor releases of
|
|
|
418
422
|
gem as new git features are adopted or as maintaining backward compatibility becomes
|
|
419
423
|
impractical. Such changes will be documented in the CHANGELOG and release notes.
|
|
420
424
|
|
|
425
|
+
### Deprecation policy
|
|
426
|
+
|
|
427
|
+
This gem removes an API only in a major release, and only after a normal release
|
|
428
|
+
deprecated it with a runtime warning and documented its replacement in
|
|
429
|
+
[UPGRADING.md](UPGRADING.md). A normal release is one that is not a pre-release. The
|
|
430
|
+
warning names the major release that removes the API once that is decided. Until then
|
|
431
|
+
it says the API will be removed in a future major release.
|
|
432
|
+
|
|
433
|
+
The recommended way to upgrade across a major version:
|
|
434
|
+
|
|
435
|
+
1. Upgrade to the latest release of the major series you are on.
|
|
436
|
+
2. Set `GIT_DEPRECATION_BEHAVIOR=raise` (or `Git::Deprecation.behavior = :raise`) in
|
|
437
|
+
your test suite and, if possible, staging.
|
|
438
|
+
3. Fix each deprecation using the entries in [UPGRADING.md](UPGRADING.md) until the
|
|
439
|
+
suite is clean.
|
|
440
|
+
4. Upgrade to the next major release.
|
|
441
|
+
|
|
442
|
+
Because every deprecation warning is present in the last release of a major series,
|
|
443
|
+
this procedure finds every change the next major requires. The version-specific steps
|
|
444
|
+
are in [UPGRADING.md](UPGRADING.md#upgrading-to-v600). See
|
|
445
|
+
[Deprecations](#deprecations) for how to configure the warnings.
|
|
446
|
+
|
|
447
|
+
### Release support policy
|
|
448
|
+
|
|
449
|
+
All development happens on `main`, which releases the next version of the gem,
|
|
450
|
+
including the next major version.
|
|
451
|
+
|
|
452
|
+
The most recent previous major series is maintained on a branch named for that
|
|
453
|
+
series, currently `4.x`. It receives bug fixes and security fixes, and
|
|
454
|
+
backward-compatible features at the maintainers' discretion. Fixes land on `main`
|
|
455
|
+
first and are backported, except a fix for a problem that exists only in the
|
|
456
|
+
maintenance branch, which targets that branch directly.
|
|
457
|
+
|
|
458
|
+
Support for a major series ends when the second major after it is released. v4.x is
|
|
459
|
+
supported until v6.0.0 ships, and v5.x until v7.0.0.
|
|
460
|
+
|
|
421
461
|
## Project announcements
|
|
422
462
|
|
|
423
463
|
### 2026-08-23: v5.x deprecations and the v6.0.0 roadmap
|
|
@@ -426,10 +466,11 @@ The road to v6.0.0 is now planned and public. The remaining ActiveRecord-style
|
|
|
426
466
|
classes (`Git::Branch`, `Git::Remote`, `Git::Stash`, `Git::Worktree`,
|
|
427
467
|
`Git::Object::Tag`, `Git::Status`, `Git::Author`, and their collections) will be
|
|
428
468
|
deprecated during the v5.x series in favor of the immutable `*Info` value-object
|
|
429
|
-
APIs. v6.0.0 will remove each deprecated class
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
floors: git ≥ 2.42.0,
|
|
469
|
+
APIs. v6.0.0 will remove each deprecated class once a normal v5.x release has carried
|
|
470
|
+
its deprecation warning and UPGRADING.md entry, per the
|
|
471
|
+
[Deprecation policy](#deprecation-policy). v6.0.0 will not ship until every planned
|
|
472
|
+
deprecation has shipped that way. v6.0.0 also raises the version floors: git ≥ 2.42.0,
|
|
473
|
+
Ruby ≥ 3.4.
|
|
433
474
|
|
|
434
475
|
[Issue #1717](https://github.com/ruby-git/ruby-git/issues/1717) is the living
|
|
435
476
|
roadmap, tracking scope, sequencing, and status. If your code uses the classes
|
data/UPGRADING.md
CHANGED
|
@@ -4,6 +4,7 @@ This document covers breaking changes and migration steps when upgrading the
|
|
|
4
4
|
`git` gem to a new major version. Each section describes what changed and how
|
|
5
5
|
to update your code when upgrading from the preceding major version.
|
|
6
6
|
|
|
7
|
+
- [Upgrading to v6.0.0](#upgrading-to-v600)
|
|
7
8
|
- [Upgrading to v5.x](#upgrading-to-v5x)
|
|
8
9
|
- [Overview](#overview)
|
|
9
10
|
- [Breaking changes](#breaking-changes)
|
|
@@ -17,6 +18,31 @@ to update your code when upgrading from the preceding major version.
|
|
|
17
18
|
- [Facade method renames](#facade-method-renames)
|
|
18
19
|
- [v4.x-style configuration methods](#v4x-style-configuration-methods)
|
|
19
20
|
- [`Git` module mixin deprecations](#git-module-mixin-deprecations)
|
|
21
|
+
- [`Git::Author` deprecated](#gitauthor-deprecated)
|
|
22
|
+
- [`Git::Branch#stashes` deprecated](#gitbranchstashes-deprecated)
|
|
23
|
+
- [`Git::Repository#remotes` deprecated](#gitrepositoryremotes-deprecated)
|
|
24
|
+
- [`Git::Remote` deprecated](#gitremote-deprecated)
|
|
25
|
+
- [`Git::Commands::CatFile::Raw` `allow_unknown_type` option deprecated](#gitcommandscatfileraw-allow_unknown_type-option-deprecated)
|
|
26
|
+
- [`Git::Branch` and `Git::Branches` deprecated](#gitbranch-and-gitbranches-deprecated)
|
|
27
|
+
|
|
28
|
+
## Upgrading to v6.0.0
|
|
29
|
+
|
|
30
|
+
v6.0.0 is not yet released. This section will be completed when it ships.
|
|
31
|
+
|
|
32
|
+
v6.0.0 removes the APIs deprecated during v5.x under the project's
|
|
33
|
+
[deprecation policy](README.md#deprecation-policy).
|
|
34
|
+
[Issue 1717](https://github.com/ruby-git/ruby-git/issues/1717) tracks its scope.
|
|
35
|
+
|
|
36
|
+
To prepare:
|
|
37
|
+
|
|
38
|
+
1. Upgrade to the latest v5.x release.
|
|
39
|
+
2. Set `GIT_DEPRECATION_BEHAVIOR=raise` (or `Git::Deprecation.behavior = :raise`) in
|
|
40
|
+
your test suite and, if possible, staging.
|
|
41
|
+
3. Fix each deprecation using the entries under
|
|
42
|
+
[Deprecated methods](#deprecated-methods) until the suite is clean.
|
|
43
|
+
4. Upgrade to v6.0.0.
|
|
44
|
+
|
|
45
|
+
---
|
|
20
46
|
|
|
21
47
|
## Upgrading to v5.x
|
|
22
48
|
|
|
@@ -240,7 +266,7 @@ shim cannot forward them). Update call sites directly:
|
|
|
240
266
|
|
|
241
267
|
| v4.x call | Notes |
|
|
242
268
|
|-----------|-------|
|
|
243
|
-
| `g.lib.list_files(ref_dir)` | Walked `.git/refs/` directly. Use `g.
|
|
269
|
+
| `g.lib.list_files(ref_dir)` | Walked `.git/refs/` directly. Use `g.branch_list`, `g.tags`, or `g.remote_list` instead. |
|
|
244
270
|
|
|
245
271
|
##### Internal plumbing methods (no replacement)
|
|
246
272
|
|
|
@@ -362,4 +388,264 @@ as bare methods is deprecated:
|
|
|
362
388
|
| `include Git; global_config(name, value)` | `Git.config_set(name, value, global: true)` |
|
|
363
389
|
| `include Git; global_config` | `Git.config_list(global: true)` |
|
|
364
390
|
|
|
391
|
+
#### `Git::Author` deprecated
|
|
392
|
+
|
|
393
|
+
Starting in v5.3.0, methods that return author, committer, or tagger data —
|
|
394
|
+
`Git::Object::Commit#author`, `Git::Object::Commit#committer`,
|
|
395
|
+
`Git::Object::Tag#tagger`, and `Git::TagInfo#tagger` — return an immutable
|
|
396
|
+
`Git::AuthorInfo` value object instead of the mutable `Git::Author`.
|
|
397
|
+
|
|
398
|
+
`Git::AuthorInfo` exposes the same `name`, `email`, and `date` readers, so code
|
|
399
|
+
that only reads these attributes needs no changes. Code that mutated a
|
|
400
|
+
`Git::Author` (via `name=`, `email=`, or `date=`) must be updated:
|
|
401
|
+
`Git::AuthorInfo` is frozen, and `#with` returns a modified copy rather than
|
|
402
|
+
updating in place (e.g. `info = info.with(name: 'New Name')`).
|
|
403
|
+
|
|
404
|
+
Constructing `Git::Author` directly emits a deprecation warning naming
|
|
405
|
+
`Git::AuthorInfo` as the replacement. The class is removed in v6.0.0.
|
|
406
|
+
|
|
407
|
+
| Deprecated usage | Replacement |
|
|
408
|
+
|-----------------|-------------|
|
|
409
|
+
| `Git::Author.new('Name <email> 1627849923 +0200')` | `Git::AuthorInfo.parse('Name <email> 1627849923 +0200')` |
|
|
410
|
+
| `author.name = 'New Name'` | `author = author.with(name: 'New Name')` (returns a new object) |
|
|
411
|
+
|
|
412
|
+
#### `Git::Branch#stashes` deprecated
|
|
413
|
+
|
|
414
|
+
`Git::Branch#stashes` ignores the branch it is called on and returns every stash
|
|
415
|
+
in the repository, so `g.branch('feature').stashes` and `g.branch('main').stashes`
|
|
416
|
+
return the same entries. Call `Git::Repository#stashes_all` instead; it is the
|
|
417
|
+
query `Git::Branch#stashes` was already running.
|
|
418
|
+
|
|
419
|
+
> **Return type change:** `Git::Branch#stashes` returns a `Git::Stashes`
|
|
420
|
+
> collection of `Git::Stash` objects, newest first. `g.stashes_all` returns an
|
|
421
|
+
> array of `[index, message]` pairs, oldest first. Code that read `stash.message`
|
|
422
|
+
> from each entry should read the second element of each pair instead. Code that
|
|
423
|
+
> iterated or indexed the collection must reverse the order first, because
|
|
424
|
+
> `Git::Stashes` yields and indexes newest first while `g.stashes_all` is oldest
|
|
425
|
+
> first.
|
|
426
|
+
|
|
427
|
+
`Git::Stashes` also exposes `save`, `apply`, and `clear`. Those map to the
|
|
428
|
+
repository's `stash_save`, `stash_apply`, and `stash_clear`, which are not
|
|
429
|
+
deprecated. `Git::Stashes#apply(i)` already passed `i` to git as `stash@{i}`
|
|
430
|
+
(`0` = newest), and `g.stash_apply(i)` does the same, so that index needs no
|
|
431
|
+
conversion.
|
|
432
|
+
|
|
433
|
+
| Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
|
|
434
|
+
|-----------------------------------------------------|-------------|
|
|
435
|
+
| `g.branch(name).stashes` | `g.stashes_all` — returns `[[index, message], ...]` |
|
|
436
|
+
| `g.branch(name).stashes.each { \|s\| puts s.message }` | `g.stashes_all.reverse_each { \|_index, message\| puts message }` |
|
|
437
|
+
| `g.branch(name).stashes.all` | `g.stashes_all` |
|
|
438
|
+
| `g.branch(name).stashes.size` | `g.stashes_all.size` |
|
|
439
|
+
| `g.branch(name).stashes[i].message` (`0` = newest, `i` coerced with `to_i`) | `g.stashes_all.reverse[i.to_i][1]` |
|
|
440
|
+
| `g.branch(name).stashes.save(message)` | `g.stash_save(message)` |
|
|
441
|
+
| `g.branch(name).stashes.apply` | `g.stash_apply` |
|
|
442
|
+
| `g.branch(name).stashes.apply(i)` (`0` = newest) | `g.stash_apply(i)` |
|
|
443
|
+
| `g.branch(name).stashes.clear` | `g.stash_clear` |
|
|
444
|
+
#### `Git::Repository#remotes` deprecated
|
|
445
|
+
|
|
446
|
+
`Git::Repository#remotes` is deprecated in favor of `Git::Repository#remote_list`
|
|
447
|
+
and is removed in v6.0.0. Its return value is unchanged. Calling `remotes` emits
|
|
448
|
+
one deprecation warning for itself plus one `Git::Remote` constructor warning for
|
|
449
|
+
each remote it returns (see the `Git::Remote` deprecation below), so a repository
|
|
450
|
+
with N remotes produces N + 1 warnings per call.
|
|
451
|
+
|
|
452
|
+
> **Return type change:** `remotes` returns `Array<Git::Remote>` — mutable
|
|
453
|
+
> objects with `name`, `url`, and `fetch_opts` accessors and `fetch`, `merge`,
|
|
454
|
+
> `branch`, and `remove` operations. `remote_list` returns
|
|
455
|
+
> `Array<Git::RemoteInfo>` — immutable value objects read from the repository's
|
|
456
|
+
> git config, with fields such as `name`, `url`, `push_url`, `fetch`, and `push`.
|
|
457
|
+
> Because a remote may carry more than one URL or refspec, `url`, `push_url`,
|
|
458
|
+
> `fetch`, and `push` are always frozen `Array<String>`. When a remote has more
|
|
459
|
+
> than one URL, git fetches from the first; the legacy `Git::Remote#url` returned
|
|
460
|
+
> the last one configured, so use `r.url.last` to reproduce that exact value.
|
|
461
|
+
> Likewise, `Git::Remote#fetch_opts` returned only the last configured fetch
|
|
462
|
+
> refspec, while `fetch` holds all of them. Operations that lived on
|
|
463
|
+
> `Git::Remote` are called on the repository with the remote name instead.
|
|
464
|
+
>
|
|
465
|
+
> **Order change:** `remotes` lists remotes in the order `git remote` prints
|
|
466
|
+
> them, while `remote_list` keeps the order in which remotes first appear in the
|
|
467
|
+
> config. When the legacy order matters, iterate `g.remote_names` (the same
|
|
468
|
+
> `git remote` order) or sort `g.remote_list` explicitly.
|
|
469
|
+
|
|
470
|
+
| Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
|
|
471
|
+
|-----------------------------------------------------|-------------|
|
|
472
|
+
| `g.remotes` | `g.remote_list` — returns `Array<Git::RemoteInfo>` |
|
|
473
|
+
| `g.remotes.map(&:name)` | `g.remote_list.map(&:name)` or `g.remote_names` |
|
|
474
|
+
| `g.remotes.map(&:to_s)` | `g.remote_list.map(&:name)` — `Git::RemoteInfo#to_s` is not the name |
|
|
475
|
+
| `g.remotes.map(&:url)` | `g.remote_list.map { \|r\| r.url.first }` — `url` is an `Array<String>` |
|
|
476
|
+
| `g.remotes.map(&:fetch_opts)` | `g.remote_list.map { \|r\| r.fetch.last }` — `fetch` holds every refspec |
|
|
477
|
+
| `g.remotes.each(&:fetch)` | `g.remote_names.each { \|name\| g.fetch(name) }` — same order as `remotes` |
|
|
478
|
+
| `remote.fetch` | `g.fetch(remote.name)` |
|
|
479
|
+
| `remote.fetch(opts)` | `g.fetch(remote.name, opts)` — same options hash |
|
|
480
|
+
| `remote.merge` | `g.merge("#{remote.name}/#{g.current_branch}")` |
|
|
481
|
+
| `remote.merge(branch)` | `g.merge("#{remote.name}/#{branch}")` |
|
|
482
|
+
| `remote.branch` | `g.branch_list("#{remote.name}/#{g.current_branch}").first` — returns a `Git::BranchInfo` |
|
|
483
|
+
| `remote.branch(name)` | `g.branch_list("#{remote.name}/#{name}").first` — returns a `Git::BranchInfo` |
|
|
484
|
+
| `remote.remove` | `g.remote_remove(remote.name)` |
|
|
485
|
+
|
|
486
|
+
#### `Git::Remote` deprecated
|
|
487
|
+
|
|
488
|
+
`Git::Remote`, `Git::Repository#remote`, and `Git::Repository#config_remote` are
|
|
489
|
+
deprecated and are removed in v6.0.0. Read a remote's configuration through
|
|
490
|
+
`Git::Repository#remote_list`, which returns one `Git::RemoteInfo` value object per
|
|
491
|
+
remote, and call the repository-level operations (`fetch`, `merge`, `branch_list`,
|
|
492
|
+
`remote_remove`) with the remote name. Return values are unchanged. Constructing a
|
|
493
|
+
`Git::Remote` directly emits one deprecation warning, and so does calling
|
|
494
|
+
`g.config_remote`. Calling `g.remote` emits two: one for `Git::Repository#remote`
|
|
495
|
+
and one for the `Git::Remote` it constructs. Likewise `g.remotes` emits one warning
|
|
496
|
+
for itself plus one per `Git::Remote` it returns (N + 1 for N remotes). The extra
|
|
497
|
+
warnings from `g.remote` and `g.remotes` are expected, not a bug.
|
|
498
|
+
|
|
499
|
+
> **Return type changes:** `Git::RemoteInfo#url` and `Git::RemoteInfo#fetch` are
|
|
500
|
+
> frozen `Array<String>` because a remote may carry more than one URL or fetch
|
|
501
|
+
> refspec. The legacy `Git::Remote#url` and `Git::Remote#fetch_opts` returned only
|
|
502
|
+
> the last configured value, so `r.url.last` and `r.fetch.last` reproduce them
|
|
503
|
+
> exactly; `r.url.first` is the URL git actually fetches from.
|
|
504
|
+
> `config_remote` returned a flat `Hash{String => String}` in which a repeated
|
|
505
|
+
> `url` or `fetch` key overwrote the earlier value, so it could not report every
|
|
506
|
+
> configured URL or refspec; `remote_list` keeps all of them. In the other
|
|
507
|
+
> direction, `Git::RemoteInfo` models only the remote variables git defines and
|
|
508
|
+
> drops any other `remote.<name>.*` key, while `config_remote` returned every key.
|
|
509
|
+
> Code that reads custom keys should filter `g.config_list` instead (see the
|
|
510
|
+
> table); that yields the same `Hash{String => String}` as `config_remote`.
|
|
511
|
+
> `Git::Remote#branch` returned a `Git::Branch`. Its replacement,
|
|
512
|
+
> `g.branch_list("#{name}/#{branch}").first`, returns a `Git::BranchInfo` value
|
|
513
|
+
> object, or `nil` when the remote-tracking branch does not exist.
|
|
514
|
+
|
|
515
|
+
In the table, `name` is the remote name (`g.remote` defaults it to `'origin'`).
|
|
516
|
+
|
|
517
|
+
| Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
|
|
518
|
+
|-----------------------------------------------------|-------------|
|
|
519
|
+
| `g.remote` | `g.remote_list.find { \|r\| r.name == 'origin' }` — returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
|
|
520
|
+
| `g.remote(name)` | `g.remote_list.find { \|r\| r.name == name }` — returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
|
|
521
|
+
| `g.config_remote(name)` for `url`, `fetch`, and the other modeled fields | `g.remote_list.find { \|r\| r.name == name }` — a `Git::RemoteInfo`, not a `Hash` |
|
|
522
|
+
| `g.config_remote(name)` for every key, including custom ones | `g.config_list.select { \|e\| e.key.start_with?("remote.#{name}.") }.to_h { \|e\| [e.key.delete_prefix("remote.#{name}."), e.value] }` — the same `Hash{String => String}` |
|
|
523
|
+
| `remote.name`, `remote.to_s` | `g.remote_list.find { \|r\| r.name == name }.name` or `g.remote_names` |
|
|
524
|
+
| `remote.url` | `g.remote_list.find { \|r\| r.name == name }.url` — `Array<String>`; `.first` for the single-URL case |
|
|
525
|
+
| `remote.fetch_opts` | `g.remote_list.find { \|r\| r.name == name }.fetch` — `Array<String>` of refspecs |
|
|
526
|
+
| `remote.fetch` | `g.fetch(name)` |
|
|
527
|
+
| `remote.fetch(opts)` | `g.fetch(name, opts)` — same option keys |
|
|
528
|
+
| `remote.merge` | `g.merge("#{name}/#{g.current_branch}")` |
|
|
529
|
+
| `remote.merge(branch)` | `g.merge("#{name}/#{branch}")` |
|
|
530
|
+
| `remote.branch` | `g.branch_list("#{name}/#{g.current_branch}").first` — returns a `Git::BranchInfo` |
|
|
531
|
+
| `remote.branch(branch)` | `g.branch_list("#{name}/#{branch}").first` — returns a `Git::BranchInfo` |
|
|
532
|
+
| `remote.remove` | `g.remote_remove(name)` |
|
|
533
|
+
|
|
534
|
+
#### `Git::Commands::CatFile::Raw` `allow_unknown_type` option deprecated
|
|
535
|
+
|
|
536
|
+
The `allow_unknown_type:` option of `Git::Commands::CatFile::Raw` is deprecated
|
|
537
|
+
and is removed in v6.0.0. Passing it emits a deprecation warning; the
|
|
538
|
+
`--allow-unknown-type` flag still reaches git unchanged until the option is
|
|
539
|
+
removed.
|
|
540
|
+
|
|
541
|
+
There is no replacement. Git 2.50 removed the unknown-type feature, so on git
|
|
542
|
+
2.50 and later `--allow-unknown-type` is an accepted no-op and the option has no
|
|
543
|
+
effect. On git 2.28 through 2.49 the flag still lets `t: true` and `s: true`
|
|
544
|
+
report the type and size of an object whose type git does not recognize, but
|
|
545
|
+
that behavior is dropped together with the option. The class is internal
|
|
546
|
+
(`@api private`) and no `Git::Repository` method passes the option, so only code
|
|
547
|
+
that constructs the command class directly is affected.
|
|
548
|
+
|
|
549
|
+
| Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
|
|
550
|
+
|-----------------------------------------------------|-------------|
|
|
551
|
+
| `Git::Commands::CatFile::Raw.new(ctx).call(sha, t: true, allow_unknown_type: true)` | `Git::Commands::CatFile::Raw.new(ctx).call(sha, t: true)` |
|
|
552
|
+
| `Git::Commands::CatFile::Raw.new(ctx).call(sha, s: true, allow_unknown_type: true)` | `Git::Commands::CatFile::Raw.new(ctx).call(sha, s: true)` |
|
|
553
|
+
|
|
554
|
+
#### `Git::Branch` and `Git::Branches` deprecated
|
|
555
|
+
|
|
556
|
+
`Git::Branch`, `Git::Branches`, `Git::Repository#branch`, and
|
|
557
|
+
`Git::Repository#branches` are deprecated and are removed in v6.0.0. Read branch
|
|
558
|
+
data through `Git::Repository#branch_list`, which returns one `Git::BranchInfo`
|
|
559
|
+
value object per local and remote-tracking branch, and call the repository-level
|
|
560
|
+
operations (`checkout`, `branch_new`, `branch_delete`, `merge`, `merge_into`,
|
|
561
|
+
`in_branch`, and so on) with the branch name. Calling `g.branch` or `g.branches`,
|
|
562
|
+
constructing a `Git::Branches`, and calling any operation on a `Git::Branch` each
|
|
563
|
+
emit a deprecation warning; their return values are unchanged. The `full`,
|
|
564
|
+
`name`, `remote`, `to_s`, and `to_a` readers on `Git::Branch` do not warn.
|
|
565
|
+
|
|
566
|
+
> **Return shape change:** `Git::Branch` exposes `full` (`main` or
|
|
567
|
+
> `remotes/origin/main`), `name`, and `remote` (a `Git::Remote`, or `nil`).
|
|
568
|
+
> `Git::BranchInfo` exposes `refname` (always the full ref: `refs/heads/main` or
|
|
569
|
+
> `refs/remotes/origin/main`), `short_name` (`main` for both), `remote_name` (a
|
|
570
|
+
> `String`, or `nil`), `remote?`, `current?`, `target_oid`, `upstream`,
|
|
571
|
+
> `worktree_path`, and `symref`. `Git::BranchInfo#to_s` is the full ref, not the
|
|
572
|
+
> `remotes/origin/main` form `Git::Branch#to_s` returned. `branch_list` takes
|
|
573
|
+
> `git branch --list` patterns: `'main'` matches the local branch and
|
|
574
|
+
> `'origin/main'` matches the remote-tracking branch. The `remotes/origin/main`
|
|
575
|
+
> and `refs/...` forms that `g.branches[...]` accepted match nothing.
|
|
576
|
+
>
|
|
577
|
+
> **`checkout` no longer creates the branch:** `g.branch('x').checkout` created
|
|
578
|
+
> `x` when it did not exist, ignoring any error from that attempt, and then
|
|
579
|
+
> checked it out. `g.checkout('x')` does not create a missing local branch,
|
|
580
|
+
> with one exception that is git's own: when exactly one remote has a branch
|
|
581
|
+
> named `x`, git creates a local tracking branch from it (its default guess
|
|
582
|
+
> behavior). Otherwise the checkout fails. To reproduce create-or-checkout,
|
|
583
|
+
> call `g.branch_new('x') unless g.local_branch?('x')` and then
|
|
584
|
+
> `g.checkout('x')`. Use `g.checkout('x', new_branch: true)` only when `x` is
|
|
585
|
+
> known not to exist; like `g.branch_new('x')`, it fails when `x` already
|
|
586
|
+
> exists. Likewise `g.branch('x').create` ignored every error, while
|
|
587
|
+
> `g.branch_new('x')` raises `Git::FailedError` when `x` already exists.
|
|
588
|
+
>
|
|
589
|
+
> **`in_branch` and `merge_into` differences:**
|
|
590
|
+
>
|
|
591
|
+
> 1. **Branch creation.** `g.branch('x').in_branch { ... }` created `x` if it did
|
|
592
|
+
> not exist. `g.in_branch('x') { ... }` raises `ArgumentError` unless `x` is an
|
|
593
|
+
> existing local branch, so call `g.branch_new('x')` first. A commit SHA, tag,
|
|
594
|
+
> or remote-tracking name is also rejected before any checkout.
|
|
595
|
+
> 2. **Detached HEAD.** `Git::Branch#in_branch` recorded the literal `HEAD` and
|
|
596
|
+
> could not restore a detached HEAD to its original commit. `g.in_branch` and
|
|
597
|
+
> `g.merge_into` record the SHA and restore it.
|
|
598
|
+
> 3. **Unborn HEAD.** Both new methods raise `Git::Error` before checking anything
|
|
599
|
+
> out when HEAD is on a branch with no commits. The old methods failed later,
|
|
600
|
+
> mid-flow.
|
|
601
|
+
> 4. **Merge overload.** `g.branch('main').merge('feature')` returned stdout from
|
|
602
|
+
> the final restore checkout and ran a hard reset after the merge.
|
|
603
|
+
> `g.merge_into('main', 'feature')` returns the merge's stdout and does no
|
|
604
|
+
> reset. It also rejects the `:no_commit` option; callers who need
|
|
605
|
+
> `--no-commit` use `checkout` and `merge` directly.
|
|
606
|
+
> 5. **Remote-tracking receivers.** Called on a remote-tracking `Git::Branch`,
|
|
607
|
+
> `in_branch` and `merge(branch)` checked out the remote-tracking ref,
|
|
608
|
+
> detaching HEAD, and any commit made there was left dangling. `g.in_branch`
|
|
609
|
+
> and `g.merge_into` take an existing local branch only. Create one from the
|
|
610
|
+
> remote-tracking ref first, with
|
|
611
|
+
> `g.branch_new(name, "remotes/#{remote}/#{name}")`, and pass that branch.
|
|
612
|
+
|
|
613
|
+
In the table, `name` is the branch name (`g.branch` defaults it to the current
|
|
614
|
+
branch), `remote` is the remote name of a remote-tracking branch, `b` is a
|
|
615
|
+
`Git::Branch`, and `info` is the `Git::BranchInfo` that replaces it. Where a
|
|
616
|
+
row says to pass `info.refname` for a remote-tracking branch, `b.full` (the
|
|
617
|
+
`remotes/<remote>/<name>` form) works too; the shorter `"#{remote}/#{name}"`
|
|
618
|
+
can resolve a local branch of that name and is only used where git expects it
|
|
619
|
+
(`branch_delete` with `remotes: true`).
|
|
620
|
+
|
|
621
|
+
| Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
|
|
622
|
+
|-----------------------------------------------------|-------------|
|
|
623
|
+
| `g.branch(name)` | `g.branch_list(name).first` for a local branch, or `g.branch_list("#{remote}/#{name}").find(&:remote?)` for a remote-tracking one — a `Git::BranchInfo`, or `nil` when the branch does not exist; the `remotes/` and `refs/` forms match nothing |
|
|
624
|
+
| `g.branch` | `g.branch_list(g.current_branch).first` — `nil` when HEAD is detached or unborn; use `g.current_branch_state` there |
|
|
625
|
+
| `g.branches` | `g.branch_list` — returns `Array<Git::BranchInfo>` |
|
|
626
|
+
| `g.branches[name]` | `g.branch_list(name).first`, or `g.branch_list("#{remote}/#{name}").find(&:remote?)` for a remote-tracking branch |
|
|
627
|
+
| `g.branches.local` | `g.branch_list.reject(&:remote?)` |
|
|
628
|
+
| `g.branches.remote` | `g.branch_list.select(&:remote?)` |
|
|
629
|
+
| `g.branches.size` | `g.branch_list.size` |
|
|
630
|
+
| `g.branches.each { \|b\| ... }` | `g.branch_list.each { \|info\| ... }` |
|
|
631
|
+
| `g.branches.to_s` | `g.branch_list.map { \|i\| "#{i.current? ? '* ' : ' '}#{i.refname}\n" }.join` — full refs, not `remotes/...` |
|
|
632
|
+
| `b.full`, `b.to_s` | `info.refname` — `refs/remotes/origin/main` rather than `remotes/origin/main` |
|
|
633
|
+
| `b.to_a` | `[info.refname]` |
|
|
634
|
+
| `b.name` | `info.short_name` |
|
|
635
|
+
| `b.remote` | `info.remote_name` — a `String`, or `nil` for a local branch |
|
|
636
|
+
| `b.gcommit` | `g.gcommit(name)` — pass `info.refname` for a remote-tracking branch |
|
|
637
|
+
| `b.checkout` | `g.checkout(name)` — does not create the branch (see above); pass `info.refname` for a remote-tracking branch |
|
|
638
|
+
| `b.create` | `g.branch_new(name)` — raises when the branch already exists |
|
|
639
|
+
| `b.delete` (local) | `g.branch_delete(name)` |
|
|
640
|
+
| `b.delete` (remote-tracking) | `g.branch_delete("#{remote}/#{name}", remotes: true)` |
|
|
641
|
+
| `b.current` | `g.current_branch == name` |
|
|
642
|
+
| `b.contains?(commit)` | `!g.branch_contains(commit, name).empty?` |
|
|
643
|
+
| `b.merge` | `g.merge(name)` |
|
|
644
|
+
| `b.merge(branch, message)` | `g.merge_into(name, branch, message)` — local `b` only; see the differences above |
|
|
645
|
+
| `b.update_ref(commit)` (local) | `g.update_ref(name, commit)` |
|
|
646
|
+
| `b.update_ref(commit)` (remote-tracking) | `g.update_ref("remotes/#{remote}/#{name}", commit)` |
|
|
647
|
+
| `b.archive(file, opts)` | `g.archive(name, file, opts)` — pass `info.refname` for a remote-tracking branch |
|
|
648
|
+
| `b.in_branch(message) { ... }` | `g.in_branch(name, message) { ... }` — local `b` only; see the differences above |
|
|
649
|
+
| `b.stashes` | `g.stashes_all` — see [`Git::Branch#stashes` deprecated](#gitbranchstashes-deprecated) |
|
|
650
|
+
|
|
365
651
|
---
|
data/lib/git/author.rb
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
module Git
|
|
4
4
|
# An author in a Git commit
|
|
5
5
|
#
|
|
6
|
+
# @deprecated Use {Git::AuthorInfo} instead; this mutable class will be
|
|
7
|
+
# removed in v6.0.0
|
|
8
|
+
#
|
|
6
9
|
# @api public
|
|
7
10
|
#
|
|
8
11
|
class Author
|
|
@@ -24,7 +27,15 @@ module Git
|
|
|
24
27
|
#
|
|
25
28
|
# @return [void]
|
|
26
29
|
#
|
|
30
|
+
# @deprecated Use {Git::AuthorInfo.parse} instead
|
|
31
|
+
#
|
|
27
32
|
def initialize(author_string)
|
|
33
|
+
if defined?(Git::Deprecation)
|
|
34
|
+
Git::Deprecation.warn(
|
|
35
|
+
'Git::Author is deprecated and will be removed in v6.0.0. Use Git::AuthorInfo instead.'
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
28
39
|
return unless (m = /(.*?) <(.*?)> (\d+) (.*)/.match(author_string))
|
|
29
40
|
|
|
30
41
|
@name = m[1]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
# Immutable value object representing an author or committer identity
|
|
5
|
+
#
|
|
6
|
+
# This is a lightweight, immutable data structure holding the identity data
|
|
7
|
+
# git records for commit authors, committers, and taggers. It replaces the
|
|
8
|
+
# mutable {Git::Author}, which is deprecated.
|
|
9
|
+
#
|
|
10
|
+
# @example Construct from individual values
|
|
11
|
+
# info = Git::AuthorInfo.new(
|
|
12
|
+
# name: 'John Doe',
|
|
13
|
+
# email: 'john.doe@example.com',
|
|
14
|
+
# date: Time.at(1627849923)
|
|
15
|
+
# )
|
|
16
|
+
# info.name #=> 'John Doe'
|
|
17
|
+
#
|
|
18
|
+
# @example Parse from a raw git author string
|
|
19
|
+
# info = Git::AuthorInfo.parse('John Doe <john.doe@example.com> 1627849923 +0200')
|
|
20
|
+
# info.email #=> 'john.doe@example.com'
|
|
21
|
+
# info.date.to_i #=> 1627849923
|
|
22
|
+
#
|
|
23
|
+
# @see Git::Object::Commit#author
|
|
24
|
+
#
|
|
25
|
+
# @see Git::Object::Commit#committer
|
|
26
|
+
#
|
|
27
|
+
# @api public
|
|
28
|
+
#
|
|
29
|
+
# @!attribute [r] name
|
|
30
|
+
# @return [String, nil] the person's name, or `nil` if not available
|
|
31
|
+
#
|
|
32
|
+
# @!attribute [r] email
|
|
33
|
+
# @return [String, nil] the person's email address, or `nil` if not available
|
|
34
|
+
#
|
|
35
|
+
# @!attribute [r] date
|
|
36
|
+
# @return [Time, nil] the timestamp of the change, or `nil` if not available
|
|
37
|
+
#
|
|
38
|
+
AuthorInfo = Data.define(:name, :email, :date) do
|
|
39
|
+
# Parses a raw git identity string into a Git::AuthorInfo
|
|
40
|
+
#
|
|
41
|
+
# The expected format is `"Name <email> timestamp offset"` as emitted by
|
|
42
|
+
# `git cat-file` for the `author`, `committer`, and `tagger` headers. The
|
|
43
|
+
# timestamp is interpreted as seconds since the Unix epoch; the timezone
|
|
44
|
+
# offset is not preserved in the resulting `date`.
|
|
45
|
+
#
|
|
46
|
+
# @example Parse a well-formed identity string
|
|
47
|
+
# Git::AuthorInfo.parse('John Doe <john.doe@example.com> 1627849923 +0200')
|
|
48
|
+
# #=> #<data Git::AuthorInfo name="John Doe", email="john.doe@example.com", ...>
|
|
49
|
+
#
|
|
50
|
+
# @example A string that does not match the expected format
|
|
51
|
+
# Git::AuthorInfo.parse('garbage')
|
|
52
|
+
# #=> #<data Git::AuthorInfo name=nil, email=nil, date=nil>
|
|
53
|
+
#
|
|
54
|
+
# @param author_string [String] the raw identity string to parse
|
|
55
|
+
#
|
|
56
|
+
# @return [Git::AuthorInfo] the parsed identity; all attributes are `nil`
|
|
57
|
+
# when the string does not match the expected format
|
|
58
|
+
#
|
|
59
|
+
def self.parse(author_string)
|
|
60
|
+
match = /(.*?) <(.*?)> (\d+) (.*)/.match(author_string)
|
|
61
|
+
return new(name: nil, email: nil, date: nil) unless match
|
|
62
|
+
|
|
63
|
+
new(name: match[1], email: match[2], date: Time.at(match[3].to_i))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|