Skip to content

Commit 15ff664

Browse files
committed
Prepare the upgrade to 2.0
1.7 has the API of 2.0 that can be added without breaking 1.x, and deprecates what 2.0 replaces or breaks, with the messages of 2.0: code without deprecations with 1.7 runs with 2.0 too, so a library can require "^1.7 || ^2.0" with a single code path. 1.7 breaks nothing of 1.6: the parameters keep their names, the arguments added to the methods of classes that aren't final are read with func_get_arg(), and gitlib keeps calling the deprecated methods a subclass overrides. It requires PHP 8.1, for the DiffLineType enum, and symfony/deprecation-contracts.
1 parent c1476cb commit 15ff664

68 files changed

Lines changed: 3629 additions & 446 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/tests.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Tests
22

33
on:
44
push:
5-
branches: [ main ]
5+
branches: [ main, 1.x ]
66
pull_request:
77

88
jobs:
@@ -12,11 +12,11 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
php: ['8.1', '8.2', '8.3', '8.4']
15+
php: ['8.1', '8.2', '8.3', '8.4', '8.5']
1616
composer-flags: ['']
1717
name: ['']
1818
include:
19-
- php: '8.0'
19+
- php: '8.1'
2020
composer-flags: '--prefer-lowest'
2121
name: '(prefer lowest dependencies)'
2222

‎CHANGELOG.md‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented in this file.
4+
5+
## [Unreleased]
6+
7+
Changes since [v1.6.0](https://github.com/gitonomy/gitlib/releases/tag/v1.6.0).
8+
9+
1.7 prepares the upgrade to 2.0: it has the API of 2.0 that can be added without breaking 1.x,
10+
and deprecates what 2.0 replaces or breaks. Code without deprecations with 1.7 runs with 2.0
11+
too, so a library can require `^1.7 || ^2.0` with a single code path. The exceptions, the
12+
changes of 2.0 1.7 can't bring, are listed in the `UPGRADE-2.0.md` of 2.0.
13+
14+
1.7 breaks nothing of 1.6: the parameters keep their names, the arguments added to the methods
15+
of classes that aren't final are read with `func_get_arg()`, commented out in their signature
16+
(`@psalm-variadic` for Psalm), and gitlib keeps calling the deprecated methods that a subclass
17+
overrides. Deprecations are triggered through `symfony/deprecation-contracts`, a new
18+
dependency.
19+
20+
1.7 requires PHP 8.1, for the `DiffLineType` enum: on PHP 8.0, Composer keeps 1.6.
21+
22+
### Features
23+
24+
- `Gitonomy\Git\Driver\Cli\CliDriver` configures git: the binary, the environment variables,
25+
the timeout and the logger. It takes the arguments of the `CliDriver` of 2.0.
26+
- `new Repository($dir, $workingDir, $driver)`: the second argument may be the working
27+
directory, the third one is a `CliDriver`.
28+
- `Repository::init()`, `initBare()`, `cloneFrom()`, `cloneBareFrom()`, `mirrorFrom()`,
29+
`isValidRepository()` and `isValidRepositoryAndBranch()` take the URL before the path and a
30+
`CliDriver` as last argument. Like in 2.0, `isValidRepositoryAndBranch()` checks the exact
31+
name of the branch, and both read a relative path as a path, never as the name of a remote
32+
of the working directory of PHP.
33+
- `Repository::getDiff($from, $to)`, with two arguments, compares two revisions, `Repository::getMergeBase()`
34+
returns their best common ancestor, `WorkingCopy::getDiff()` compares the working tree with
35+
a revision. Like in 2.0, they throw a `ReferenceNotFoundException` for an unknown revision,
36+
as `WorkingCopy::checkoutNewBranch()`.
37+
- `Diff::parse()`, `Diff::fromArray()` and `File::fromArray()` take the repository to read the
38+
blobs from, the constructor of `Diff\File` the changes and the repository.
39+
`File::isModeChange()`, `File::isCopy()`, `FileChange::getAdditions()` and
40+
`FileChange::getDeletions()`.
41+
- `FileChange::getDiffLines()` returns the lines of a hunk as `Diff\DiffLine` objects, with
42+
`getType()`, a `Diff\DiffLineType`, and `getContent()`. The constructor of `FileChange`
43+
takes `DiffLine` objects.
44+
- `Repository::getBlame()` and `Blame` take the first and the last lines to blame, as
45+
integers.
46+
`Blame::getHunks()` returns `Blame\Hunk` objects, numbered like the file;
47+
`Blame\Line::getNumber()` and `getOriginalNumber()`.
48+
- `Log::withOffset()` and `withLimit()` return another log.
49+
- `Branch::isMergedInto()` and `Branch::moveTo()`. `ReferenceBag::createBranch()` and
50+
`createTag()` take a `Revision` too.
51+
- `PushReference::getFullname()`, `getRange()`, `isCreation()` and `isDeletion()`.
52+
- Iterating a `Tree` gives its entries, `TreeEntry` objects with `getName()`, `getMode()` and
53+
`getObject()`, by name; `Tree::getBlobs()`, `getTrees()` and `getSubmodules()` return those
54+
of one type.
55+
- `Submodule`, with `getHash()`. The submodules of trees are still `CommitReference`
56+
objects, which extend `Submodule`.
57+
- `WorkingCopy::checkoutNewBranch()`. `WorkingCopy::checkout()` takes any `Revision`.
58+
- `Repository::getSizeInBytes()`, which doesn't follow links.
59+
60+
### Deprecations
61+
62+
- An array of options as second argument of `Repository`, the `debug` option set to `false`,
63+
`Repository::setLogger()` and `getLogger()`: use the `workingDir` and `driver` arguments.
64+
- `Admin` and `Repository::cloneTo()`: use the static methods of `Repository`.
65+
- `Repository::getDiff()` with one argument and `Log::getDiff()`: use
66+
`Repository::getDiff($from, $to)` or `WorkingCopy::getDiff()`.
67+
- `Diff::setRepository()`, `File::setRepository()`, `File::addChange()`,
68+
`File::isChangeMode()`, `File::isDelete()`, `File::getAnchor()` and `FileChange::getCount()`.
69+
- `FileChange::getLines()`, which returns the lines as arrays, the `FileChange::LINE_*`
70+
constants, and passing the lines to the constructor of `FileChange` as arrays: use
71+
`getDiffLines()`, `DiffLineType` and `DiffLine` objects.
72+
- A range of lines as a string in `Repository::getBlame()` and `Blame`,
73+
`Blame::getGroupedLines()` and `Blame\Line::getLine()`.
74+
- `Log::setOffset()`, `setLimit()`, a negative offset or limit, an option of `git log` but
75+
`--all` as revision, and `count()` of a log whose offset or limit leaves commits out: it
76+
counts them all, where 2.0 counts the commits the log iterates. Use `countCommits()`.
77+
- `ReferenceBag::update()`, `Branch::isMergedTo()`, arguments of
78+
`Commit::getIncludingBranches()`, creating a branch or a tag that exists already, deleting
79+
a reference by another name than its full name under `refs/`.
80+
- Passing an argument to `Repository::getReferences()`, to read the references again: open
81+
a new `Repository`.
82+
- `PushReference::getReference()`, `getRevision()`, `isCreate()` and `isDelete()`.
83+
- `CommitReference`. `Tree::getEntries()`, `getBlobEntries()`, `getTreeEntries()` and
84+
`getCommitReferenceEntries()`, which return the entries as arrays: iterate the tree, or use
85+
`getBlobs()`, `getTrees()` and `getSubmodules()`.
86+
- The second argument of `WorkingCopy::checkout()`: use `checkoutNewBranch()`. A local
87+
`Branch` object, which `checkout()` detaches HEAD on, where 2.0 attaches HEAD to the branch:
88+
pass its name, or its commit.
89+
- Passing a revision name to `Repository::getTree()` or `getBlob()` instead of a hash, and
90+
passing data to the constructor of `Commit`, internal in 2.0.
91+
- `Repository::getSize()`, `Repository::shell()`, `Commit::getShortMessage()`,
92+
`Commit::getFixedShortHash()` and `Util\StringHelper`.
93+
- The parsers that 2.0 moves to its `CliDriver`, all but `DiffParser` and `ParserBase`.
94+
- The classes `final` in 2.0 are `@final since gitonomy/gitlib 1.7`. The constructors
95+
internal in 2.0 (`Commit`, `Tree`, `Blob`, `Revision`, `Reference`, `ReferenceBag`) say so.
96+
97+
## [v1.6.0] - 2025-12-09
98+
99+
_Earlier versions are not documented here. See the
100+
[GitHub releases](https://github.com/gitonomy/gitlib/releases) and
101+
[commit history](https://github.com/gitonomy/gitlib/commits/v1.6.0) for prior changes._
102+
103+
[Unreleased]: https://github.com/gitonomy/gitlib/compare/v1.6.0...1.x
104+
[v1.6.0]: https://github.com/gitonomy/gitlib/releases/tag/v1.6.0

‎composer.json‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@
3535
}
3636
},
3737
"require": {
38-
"php": "^8.0",
38+
"php": "^8.1",
3939
"ext-pcre": "*",
40+
"symfony/deprecation-contracts": "^2.5 || ^3",
4041
"symfony/polyfill-mbstring": "^1.7",
4142
"symfony/process": "^5.4 || ^6.0 || ^7.0 || ^8.0"
4243
},
4344
"require-dev": {
4445
"ext-fileinfo": "*",
45-
"phpspec/prophecy-phpunit": "^2.0",
46+
"phpspec/prophecy-phpunit": "^2.1",
4647
"phpunit/phpunit": "^7.5.20 || ^8.5.20 || ^9.5.9",
47-
"psr/log": "^1.0"
48+
"psr/log": "^1.0",
49+
"symfony/phpunit-bridge": "^6.4 || ^7.0"
4850
},
4951
"config": {
5052
"preferred-install": "dist",

‎doc/admin.md‎

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,74 @@
11
Create and access git repositories
22
==================================
33

4-
gitlib provides methods to initialize new repositories.
4+
gitlib provides static methods of `Repository` to initialize and clone
5+
repositories. Each of them takes a `CliDriver` as last argument, to
6+
configure git (see [Repository](repository.md)).
7+
8+
`Admin` and `Repository::cloneTo()`, which did it before 1.7, are
9+
deprecated.
510

611
Create a repository
712
-------------------
813

9-
To initialize a new repository, use method `Admin::init`.
10-
1114
```php
12-
// Initialize a bare repository
13-
$repository = Gitonomy\Git\Admin::init('/path/to/repository');
15+
use Gitonomy\Git\Repository;
1416

15-
// Initialize a non-bare repository
16-
$repository = Gitonomy\Git\Admin::init('/path/to/repository', false);
17-
```
17+
// Initialize a repository with a working directory
18+
$repository = Repository::init('/path/to/repository');
1819

19-
Default behavior is to create a bare repository. If you want to
20-
initialize a repository with a working copy,pass `false` as third
21-
argument of Repository constructor.
20+
// Initialize a bare repository
21+
$repository = Repository::initBare('/path/to/repository');
22+
```
2223

2324
Cloning repositories
2425
--------------------
2526

2627
You can clone a repository from an URL by doing:
2728

2829
```php
29-
// Clone to a bare repository
30-
$repository = Gitonomy\Git\Admin::cloneTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git');
30+
// Clone to a repository with a working directory
31+
$repository = Repository::cloneFrom('https://github.com/gitonomy/gitlib.git', '/tmp/gitlib');
3132

32-
// Clone to a non-bare repository
33-
$repository = Gitonomy\Git\Admin::cloneTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git', false);
33+
// Clone to a bare repository
34+
$repository = Repository::cloneBareFrom('https://github.com/gitonomy/gitlib.git', '/tmp/gitlib');
3435
```
3536

36-
Default behavior is to clone in a bare repository.
37-
3837
You can also clone a repository and point it to a specific branch. In a
39-
non-bare repository, this branch will be checked out:
38+
repository with a working directory, this branch is checked out:
4039

4140
```php
42-
// Clone to a bare repository
43-
$repository = Gitonomy\Git\Admin::cloneBranchTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git', 'a-branch');
41+
$repository = Repository::cloneFrom('https://github.com/gitonomy/gitlib.git', '/tmp/gitlib', 'a-branch');
4442

45-
// Clone to a non-bare repository
46-
$repository = Gitonomy\Git\Admin::cloneBranchTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git', 'a-branch', false);
43+
$repository = Repository::cloneBareFrom('https://github.com/gitonomy/gitlib.git', '/tmp/gitlib', 'a-branch');
4744
```
4845

49-
Clone a Repository object
50-
-------------------------
51-
52-
If you already have a Repository instance and want to clone it, you can
53-
use this shortcut:
46+
To clone a `Repository` object, clone its git directory:
5447

5548
```php
56-
$new = $repository->cloneTo('/tmp/clone');
49+
$new = Repository::cloneBareFrom($repository->getGitDir(), '/tmp/clone');
5750
```
5851

5952
Mirror a repository
6053
-------------------
6154

62-
If you want to mirror fully a repository and all references, use the
63-
`mirrorTo` method. This method takes only two arguments, where to mirror
64-
and what to mirror:
55+
To mirror a repository and all its references into a bare repository,
56+
use the `mirrorFrom` method:
57+
58+
```php
59+
$mirror = Repository::mirrorFrom('https://github.com/gitonomy/gitlib.git', '/tmp/mirror');
60+
```
61+
62+
Check a remote repository
63+
-------------------------
64+
65+
To check that git can read a repository, without cloning it:
6566

6667
```php
67-
// Mirror to a bare repository
68-
$mirror = Gitonomy\Git\Admin::mirrorTo('/tmp/mirror', 'https://github.com/gitonomy/gitlib.git');
68+
Repository::isValidRepository('https://github.com/gitonomy/gitlib.git');
6969

70-
// Mirror to a non-bare repository
71-
$mirror = Gitonomy\Git\Admin::mirrorTo('/tmp/mirror', 'https://github.com/gitonomy/gitlib.git', false);
70+
// And that it has the branch "main", with this exact name
71+
Repository::isValidRepositoryAndBranch('https://github.com/gitonomy/gitlib.git', 'main');
7272
```
7373

7474
### References

‎doc/blame.md‎

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,28 @@ If you want to access directly a line:
2626
$line = $blame->getLine(32);
2727
```
2828

29+
To blame some lines only, pass the first and the last ones:
30+
31+
```php
32+
$blame = $repository->getBlame('master', 'README.md', 10, 20);
33+
```
34+
35+
`getLines()` and `getLine()` number these lines from 1, where gitlib 2.0
36+
numbers them like the file: use `Line::getNumber()`. A range of lines as
37+
a string (`'10,20'`, `'10,+11'`, a regular expression), passed to
38+
`git blame -L`, is deprecated.
39+
2940
The Line object
3041
---------------
3142

3243
LineObject represents an item of the blame file. It is composed of those
3344
informations:
3445

3546
```php
36-
$line->getCommit(); // returns a Commit
37-
$line->getContent(); // returns text
47+
$line->getCommit(); // returns a Commit
48+
$line->getContent(); // returns text
49+
$line->getNumber(); // the number of the line in the file
50+
$line->getOriginalNumber(); // its number in the file of the commit
3851

3952
// you can access author from commmit:
4053
$author = $line->getCommit()->getAuthorName();
@@ -46,13 +59,18 @@ Group reading by commit
4659
If you plan to display it, you'll probably need a version where lines
4760
from same commit are grouped.
4861

49-
To do so, use the *getGroupedLines* method that will return an array
50-
like this:
62+
To do so, use the *getHunks* method, which returns `Blame\Hunk` objects,
63+
the consecutive lines last changed by the same commit, by number in the
64+
file:
5165

5266
```php
53-
$blame = array(
54-
array(Commit, array(1 => Line, 2 => Line, 3 => Line)),
55-
array(Commit, array(4 => Line)),
56-
array(Commit, array(5 => Line, 6 => Line))
57-
)
67+
foreach ($blame->getHunks() as $hunk) {
68+
echo $hunk->getCommit()->getHash().PHP_EOL;
69+
70+
foreach ($hunk->getLines() as $number => $line) {
71+
echo $number.': '.$line->getContent().PHP_EOL;
72+
}
73+
}
5874
```
75+
76+
`getGroupedLines()` and `Line::getLine()` are deprecated.

‎doc/branch.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,17 @@ You can check is the branch is a local or remote one:
1414
$branch->isLocal();
1515
$branch->isRemote();
1616
```
17+
18+
To check that the history of a revision contains the branch:
19+
20+
```php
21+
$branch->isMergedInto('master');
22+
```
23+
24+
`isMergedTo()` is deprecated.
25+
26+
To point the branch to another revision:
27+
28+
```php
29+
$branch = $branch->moveTo('a8b7e4...');
30+
```

‎doc/commit.md‎

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ For example, if you want to display all parents, starting from a commit:
3131

3232
```php
3333
function displayLog(Gitonomy\Git\Commit $commit) {
34-
echo '- '.$commit->getShortMessage().PHP_EOL;
34+
echo '- '.$commit->getSubjectMessage().PHP_EOL;
3535
foreach ($commit->getParents() as $parent) {
3636
displayLog($parent);
3737
}
@@ -94,24 +94,20 @@ To access the message, you can use the *getMessage* method:
9494
$commit->getMessage();
9595
```
9696

97-
For your convenience, this library provides a shortcut method to keep
98-
only the first line or first 50 characters if the first line is too
99-
long:
97+
To keep only the first line, or its first 50 characters if it is too
98+
long, use [symfony/string](https://symfony.com/doc/current/string.html):
10099

101100
```php
102-
$commit->getShortMessage();
103-
```
101+
use function Symfony\Component\String\u;
104102

105-
You can customize it like this:
103+
u($commit->getSubjectMessage())->truncate(50, '...');
106104

107-
```php
108-
$commit->getShortMessage(45, true, '.');
105+
// Without cutting the last word
106+
u($commit->getSubjectMessage())->truncate(45, '.', false);
109107
```
110108

111-
- The first parameter is the max length of the message.
112-
- The second parameter determine if the last word should be cut or
113-
preserved
114-
- The third parameter is the separator
109+
`getShortMessage()`, which did it, is deprecated, as `getFixedShortHash()`:
110+
use `substr($commit->getHash(), 0, 6)`.
115111

116112
There are also two other methods for your convenience:
117113

@@ -165,7 +161,11 @@ Find every branches containing a commit
165161
---------------------------------------
166162

167163
```php
168-
$branches = $commit->getIncludingBranches($includeLocalBranches, $includeRemoteBranches);
169-
$localBranches = $commit->getIncludingBranches(true, false);
170-
$remoteBranches = $commit->getIncludingBranches(false, true);
164+
use Gitonomy\Git\Reference\Branch;
165+
166+
$branches = $commit->getIncludingBranches();
167+
$localBranches = array_filter($branches, static fn (Branch $branch): bool => $branch->isLocal());
168+
$remoteBranches = array_filter($branches, static fn (Branch $branch): bool => $branch->isRemote());
171169
```
170+
171+
Passing arguments to `getIncludingBranches()` is deprecated.

0 commit comments

Comments
 (0)