Skip to content

Commit 79eff7a

Browse files
authored
Merge pull request #265 from gitonomy/smooth-upgrade
Smooth the upgrade from 1.7
2 parents fc17694 + 8743ffb commit 79eff7a

42 files changed

Lines changed: 582 additions & 331 deletions

Some content is hidden

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

‎CHANGELOG.md‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ deprecated until 3.0.
1616
missing revision or object a `ReferenceNotFoundException`, a timeout a
1717
`ProcessTimedOutException`; dates are `\DateTimeImmutable`; names in diffs and trees are
1818
UTF-8; references are read again after every write gitlib makes, and peel annotated tags;
19-
the diff of a merge commit is against its first parent; blame lines, tree entries, diff
20-
lines and logs are typed objects; the subject of a message is its first paragraph. See
19+
the diff of a merge commit is against its first parent; blame lines and logs are typed
20+
objects; the subject of a message is its first paragraph. See
2121
[UPGRADE-2.0.md](UPGRADE-2.0.md) for every change.
2222
([#253](https://github.com/gitonomy/gitlib/pull/253))
2323
- Return the dates of commits and tags at the UTC offset of their author, committer or tagger,
@@ -43,6 +43,9 @@ deprecated until 3.0.
4343
`The git command "clone" failed (exit 128): fatal: ...`. The command line, the rest of the
4444
error output and the output are no longer in the message: `getErrorOutput()` and
4545
`getOutput()` return the outputs of git. ([#254](https://github.com/gitonomy/gitlib/pull/254))
46+
- `Tree::resolvePath()` throws an `InvalidArgumentException` for a path through a submodule,
47+
instead of an `Exception\UnexpectedValueException`, which gitlib no longer throws.
48+
([#254](https://github.com/gitonomy/gitlib/pull/254))
4649
- When git can't start, a missing binary for instance, `CliDriver` throws a `DriverException`
4750
instead of a `ProcessException`, with a message of one line, without the command line:
4851
`The git command "clone" could not start: ...`.
@@ -78,7 +81,9 @@ deprecated until 3.0.
7881
`Repository::getSizeInBytes()` and `WorkingCopy::getDiff()`,
7982
`WorkingCopy::checkoutNewBranch()`, `Branch::moveTo()` and `Branch::isMergedInto()`,
8083
`Log::withOffset()` and `withLimit()`, `Blame::getHunks()` and the line numbers of
81-
`Blame\Line`, `TreeEntry` and `Submodule`, `Diff\DiffLine` and `Diff\DiffLineType`,
84+
`Blame\Line`, `FileChange::getDiffLines()`, which returns `Diff\DiffLine` objects typed by
85+
`Diff\DiffLineType`, iterating a `Tree`, which gives `TreeEntry` objects,
86+
`Tree::getBlobs()`, `getTrees()` and `getSubmodules()`, `Submodule`,
8287
`File::isModeChange()`, `FileChange::getAdditions()` and `getDeletions()`,
8388
`PushReference::getFullname()`, `getRange()`, `isCreation()` and `isDeletion()`, and the SSH
8489
and X.509 signatures of tags. ([#253](https://github.com/gitonomy/gitlib/pull/253),
@@ -103,7 +108,8 @@ deprecated until 3.0.
103108
### Deprecations
104109

105110
All are removed in 3.0; see [UPGRADE-2.0.md](UPGRADE-2.0.md).
106-
([#253](https://github.com/gitonomy/gitlib/pull/253))
111+
([#253](https://github.com/gitonomy/gitlib/pull/253)) 1.7 deprecates them already: they are
112+
all deprecated since 1.7.
107113

108114
- `Admin`, replaced by the static methods of `Repository`, and `Repository::cloneTo()`,
109115
replaced by `cloneFrom()` and `cloneBareFrom()`.
@@ -116,18 +122,20 @@ All are removed in 3.0; see [UPGRADE-2.0.md](UPGRADE-2.0.md).
116122
replaced by `Branch::moveTo()` and `isMergedInto()`.
117123
- Calling `Repository::getDiff()` with one argument, `Log::getDiff()`, `Diff::setRepository()`,
118124
`File::setRepository()`, `addChange()`, `isChangeMode()`, `isDelete()` and `getAnchor()`,
119-
`FileChange::getCount()` and its `LINE_*` constants, reading diff lines as arrays.
125+
`FileChange::getCount()`, `getLines()`, replaced by `getDiffLines()`, and the `LINE_*`
126+
constants, replaced by `DiffLineType`, lines passed to the constructor of `FileChange` as
127+
arrays.
120128
- `Log::setOffset()` and `setLimit()`.
121129
- A range of lines as a string in `Repository::getBlame()`, `Blame::getGroupedLines()` and
122130
`Blame\Line::getLine()`.
123-
- `CommitReference`, `Tree::getCommitReferenceEntries()`, reading tree entries as arrays.
131+
- `CommitReference`, `Tree::getCommitReferenceEntries()`, `getEntries()`, `getBlobEntries()`
132+
and `getTreeEntries()`: iterate the tree, or use `getBlobs()`, `getTrees()` and
133+
`getSubmodules()`.
124134
- The branch to create of `WorkingCopy::checkout()`.
125135
- `Commit::getShortMessage()` and `getFixedShortHash()`, `Repository::shell()`,
126136
`PushReference::getReference()`, `getRevision()`, `isCreate()` and `isDelete()`.
127137
- `Repository::getSize()`, in kilobytes: use `getSizeInBytes()`.
128138
([#254](https://github.com/gitonomy/gitlib/pull/254))
129-
- `Exception\UnexpectedValueException`, which gitlib no longer throws.
130-
([#254](https://github.com/gitonomy/gitlib/pull/254))
131139

132140
### Security
133141

‎UPGRADE-2.0.md‎

Lines changed: 152 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ call triggers a deprecation (`E_USER_DEPRECATED`, through
1212
them. Log the deprecations of your application to find them. The other
1313
changes, listed below too, break code that relies on them.
1414

15+
Upgrade to 1.7 first, which requires PHP 8.1: it has most of the
16+
replacements, and triggers the deprecations of the API they replace (see
17+
[Supporting 1.x and 2.x](#supporting-1x-and-2x)).
18+
1519
Requirements
1620
------------
1721

@@ -28,6 +32,65 @@ Requirements
2832
* Repositories using SHA-256 hashes are refused, with an
2933
`UnsupportedOperationException`, by both drivers.
3034

35+
Supporting 1.x and 2.x
36+
----------------------
37+
38+
gitlib 1.7 has the API of 2.0 that can be added without breaking 1.x, and
39+
deprecates what 2.0 replaces or breaks: `new Repository($dir, $workingDir,
40+
$driver)` with a `CliDriver` of the same arguments, the static methods of
41+
`Repository` creating repositories, `Diff::parse($rawDiff, $repository)`,
42+
`Repository::getDiff($from, $to)`, `FileChange::getDiffLines()`, iterating
43+
a `Tree`, `Log::withLimit()`, `Branch::isMergedInto()`,
44+
`Blame::getHunks()`... Fix the deprecations of 1.7, then require
45+
`^1.7 || ^2.0`: a library supporting PHP 8.1 to 8.3 gets 1.7 there, and 2.0
46+
from PHP 8.4, with the same code.
47+
48+
Before:
49+
50+
```php
51+
$repository = new Repository($gitDir, ['working_dir' => $workingDir, 'logger' => $logger]);
52+
$diff = Diff::parse($rawDiff);
53+
$diff->setRepository($repository);
54+
```
55+
56+
After, with 1.7 and 2.0:
57+
58+
```php
59+
use Gitonomy\Git\Driver\Cli\CliDriver;
60+
61+
$repository = new Repository($gitDir, $workingDir, new CliDriver(logger: $logger));
62+
$diff = Diff::parse($rawDiff, $repository);
63+
```
64+
65+
1.7 breaks nothing of 1.x, so:
66+
67+
* Pass by position the arguments whose name changes in 2.0 (see
68+
[Extending gitlib](#extending-gitlib)): 1.7 keeps the names of 1.x. So
69+
do the arguments 1.7 adds to `Repository::getDiff()`, `getBlame()`,
70+
`Diff::parse()`, `Diff::fromArray()` and `File::fromArray()`: their
71+
signature leaves them out, not to break the classes overriding these
72+
methods.
73+
* `ReferenceBag::createBranch()` and `createTag()` keep a revision name as
74+
the hash of the reference they return, like 1.x: pass a `Revision` to
75+
get the hash of its commit with both versions.
76+
* The submodules of trees are `CommitReference` objects, which extend
77+
`Submodule` in 1.7: check them with `instanceof Submodule`.
78+
* `File::toArray()` has no `is_copy` key in 1.7.
79+
80+
What 1.7 can't bring, as it would break 1.x, behaves differently with
81+
both versions:
82+
83+
* With a range of lines, `Blame::getLines()` and `getLine()` number the
84+
lines from 1 in 1.7, like the file in 2.0: use `Line::getNumber()`, or
85+
`Blame::getHunks()`, numbered like the file in 1.7 too.
86+
* 1.7 doesn't read the references again after a write: open a new
87+
`Repository` to read them, with both versions. `getReferences(true)` is
88+
deprecated in 1.7 too.
89+
* The classes `final` in 2.0 are only `@final` in 1.7: see
90+
[Testing code using gitlib](#testing-code-using-gitlib).
91+
* The other changes listed below: dates, the stricter parser of diffs,
92+
the directory git runs in, the exceptions...
93+
3194
Opening a repository
3295
--------------------
3396

@@ -43,6 +106,16 @@ Opening a repository
43106
request. Pass the variables git needs to the `environmentVariables`
44107
argument instead.
45108

109+
git inherits `$_ENV`, and the variables of `getenv()` that `$_SERVER`
110+
holds too, like Symfony Process passes them: a variable an application
111+
only writes in `$_SERVER`, without `putenv()` nor `$_ENV`, which the
112+
option passed, no longer reaches git. A command line application
113+
relying on it passes `$_SERVER` itself:
114+
115+
```php
116+
$driver = new CliDriver(environmentVariables: array_filter($_SERVER, is_string(...)));
117+
```
118+
46119
Before:
47120

48121
```php
@@ -233,11 +306,13 @@ Diffs
233306
changes when constructed: `Diff::setRepository()`,
234307
`File::setRepository()` and `File::addChange()` are deprecated.
235308
`Diff::parse()` and `fromArray()` take the repository to read blobs from.
236-
* The lines of a hunk, `FileChange::getLines()`, are `Diff\DiffLine`
237-
objects, with `getType()`, a `Diff\DiffLineType`, and `getContent()`:
238-
reading them as arrays is deprecated, as the `FileChange::LINE_*`
239-
constants and `FileChange::getCount()`, replaced by `getAdditions()` and
240-
`getDeletions()`.
309+
* `FileChange::getDiffLines()` returns the lines of a hunk as
310+
`Diff\DiffLine` objects, with `getType()`, a `Diff\DiffLineType`, and
311+
`getContent()`: `FileChange::getLines()`, which returns them as arrays,
312+
is deprecated, as the `FileChange::LINE_*` constants and
313+
`FileChange::getCount()`, replaced by `getAdditions()` and
314+
`getDeletions()`. The constructor of `FileChange` takes `DiffLine`
315+
objects: arrays are deprecated.
241316

242317
Before:
243318

@@ -249,12 +324,12 @@ Diffs
249324
}
250325
```
251326

252-
After:
327+
After, with 1.7 and 2.0:
253328

254329
```php
255330
use Gitonomy\Git\Diff\DiffLineType;
256331

257-
foreach ($change->getLines() as $line) {
332+
foreach ($change->getDiffLines() as $line) {
258333
if (DiffLineType::Addition === $line->getType()) {
259334
// ... $line->getContent()
260335
}
@@ -273,6 +348,13 @@ Diffs
273348
`git diff --raw --patch`, with `-z` or not. It reads the names of the
274349
files from the raw information: names with spaces are no longer split at
275350
the wrong place.
351+
* It reads the status of a file, a creation, a modification, a deletion,
352+
a rename..., from its raw line (`A`, `M`, `D`, `R100`...), instead of
353+
the headers of the patch (`new file mode`...): a diff written by hand
354+
where they disagree gives another result. Like git, the raw line
355+
separates the status and the name with a TAB: spaces throw a
356+
`RuntimeException`. Generate the diffs of your tests with git, with
357+
`git diff --raw --patch --full-index`, instead of writing them.
276358
* During a merge conflict, `WorkingCopy::getDiffPending()` and
277359
`getDiffStaged()` leave the unmerged files out, where they threw, and
278360
`WorkingCopy::getDiff()` compares them, conflict markers included, to the
@@ -295,12 +377,30 @@ Blame
295377
Trees
296378
-----
297379

298-
* `Tree::getEntries()`, `getTreeEntries()`, `getBlobEntries()` and
299-
`getSubmoduleEntries()` return `TreeEntry` objects, with `getName()`,
300-
`getMode()` and `getObject()`: reading them as arrays
301-
(`[$mode, $object]`) is deprecated.
380+
* Iterating a `Tree` gives its entries by name, `TreeEntry` objects with
381+
`getName()`, `getMode()` and `getObject()`; `getBlobs()`, `getTrees()`
382+
and `getSubmodules()` return them by kind. `getEntries()`,
383+
`getBlobEntries()` and `getTreeEntries()`, which return them as arrays
384+
(`[$mode, $object]`), are deprecated.
385+
386+
Before:
387+
388+
```php
389+
foreach ($tree->getEntries() as $name => [$mode, $object]) {
390+
// ...
391+
}
392+
```
393+
394+
After, with 1.7 and 2.0:
395+
396+
```php
397+
foreach ($tree as $name => $entry) {
398+
// ... $entry->getMode(), $entry->getObject()
399+
}
400+
```
401+
302402
* `CommitReference` is deprecated: it is `Submodule`. So is
303-
`Tree::getCommitReferenceEntries()`: use `getSubmoduleEntries()`.
403+
`Tree::getCommitReferenceEntries()`: use `getSubmodules()`.
304404
* `Tree::resolvePath()` throws an `InvalidArgumentException` explaining
305405
why a path doesn't resolve, a path through a submodule included, which
306406
threw an `UnexpectedValueException`, and resolves `/` to the tree.
@@ -409,7 +509,9 @@ Errors
409509
writes on its output (`CONFLICT (content): ...`). The command line, the
410510
rest of the error output and the output are no longer in the message:
411511
`getErrorOutput()` and `getOutput()` return the outputs of git. Its
412-
constructor takes the git command as second argument, optional.
512+
constructor takes the git command as second argument, optional, and
513+
reads the exit code and the outputs of the `Process`: a test double of
514+
`Process` must return them.
413515
* When git can't start, a missing binary for instance, `CliDriver` throws
414516
a `DriverException` instead of a `ProcessException`, with a message of
415517
one line, without the command line: `The git command "clone" could not
@@ -419,9 +521,8 @@ Errors
419521
failed;
420522
* `ReferenceBag` (`Repository::getReferences()`) only returns an empty bag
421523
for a repository without references, and throws for any other failure.
422-
* `Gitonomy\Git\Exception\UnexpectedValueException` is deprecated: gitlib
423-
no longer throws it. Catch `InvalidArgumentException` for
424-
`Tree::resolvePath()`.
524+
* gitlib no longer throws `Gitonomy\Git\Exception\UnexpectedValueException`:
525+
catch `InvalidArgumentException` for `Tree::resolvePath()`.
425526
* `Commit::getShortHash()` throws a `ReferenceNotFoundException` for an
426527
unknown commit, instead of a `ProcessException`.
427528
* `Hooks` throws a `RuntimeException` when a hook exists already, or can't
@@ -555,6 +656,27 @@ Extending gitlib
555656
be unserialized: clear such caches. `Diff::toArray()` and
556657
`Diff::fromArray()` remain compatible.
557658

659+
Testing code using gitlib
660+
-------------------------
661+
662+
The classes `final` in 2.0 can't be doubled: `createMock()` of PHPUnit,
663+
Prophecy and Mockery refuse them ("Class "Gitonomy\Git\WorkingCopy" is
664+
declared "final" and cannot be doubled", "could not reflect class
665+
Gitonomy\Git\WorkingCopy as it is marked final"). Instead, from the
666+
simplest:
667+
668+
1. Construct the value objects: `Diff`, `Diff\File`, `Diff\FileChange`
669+
and `Diff\DiffLine` have public constructors. `new Diff([new File(...)],
670+
'')` works with 1.x too.
671+
2. Parse the real output of git: `Diff::parse()` of
672+
`git diff --raw --patch --full-index` (see [Diffs](#diffs)).
673+
3. Use a real repository, in a temporary directory:
674+
`Repository::init($path)`, or `git init` with 1.x before 1.7. A
675+
`WorkingCopy` needs one.
676+
4. Implement `Gitonomy\Git\Driver\DriverInterface`, and pass it as the
677+
driver of `Repository`: see
678+
["Using your own driver"](doc/repository.md#using-your-own-driver).
679+
558680
Data returned
559681
-------------
560682

@@ -590,9 +712,20 @@ Data returned
590712
Other changes
591713
-------------
592714

593-
* Relative paths, in the arguments of `Repository::run()` and in the paths
594-
of a log, are relative to the repository instead of the current directory
595-
of PHP.
715+
* `Repository::run()` and `runProcess()` run git in the working directory
716+
of the repository, or its git directory when it is bare, instead of the
717+
current directory of PHP. Relative paths, in the arguments of `run()`
718+
and in the paths of a log, are relative to the repository. When PHP runs
719+
in a subdirectory of the repository, the output of the commands relative
720+
to the current directory changes too, and so does their default scope:
721+
722+
| `run()` in `repository/sub` | 1.x | 2.0 |
723+
|------------------------------|-------------------|-------------------------------|
724+
| `ls-files` | `in-sub.txt` | `root.txt`, `sub/in-sub.txt` |
725+
| `status --short` | ` M ../root.txt` | ` M root.txt` |
726+
| `rev-parse --show-prefix` | `sub/` | (empty) |
727+
728+
Nothing changes when PHP runs out of the repository.
596729
* `Hooks::set()` makes hooks executable by their owner only (`0700`,
597730
instead of `0777`): change their permissions if another user runs them.
598731
* `Repository::shell()` is deprecated: it ignored the driver. Run

‎composer.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,10 @@
5555
"config": {
5656
"preferred-install": "dist",
5757
"sort-packages": true
58+
},
59+
"extra": {
60+
"branch-alias": {
61+
"dev-main": "2.0.x-dev"
62+
}
5863
}
5964
}

‎doc/admin.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Create and access git repositories
22
==================================
33

44
The static methods of `Repository` create new repositories, and return them.
5-
They replace `Admin`, deprecated since 2.0: see
5+
They replace `Admin`, deprecated since 1.7: see
66
[UPGRADE-2.0.md](../UPGRADE-2.0.md#creating-repositories).
77

88
To open a repository that exists already, use `new Repository($path)`: see

‎doc/blame.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ throws a `DriverException`. A last line before the first one blames the lines
2626
in order, like `git blame -L 5,3`.
2727

2828
Passing a range as a string (`'2,3'` or `'2,+2'`, like `git blame -L`) is
29-
deprecated since 2.0: pass the first and the last lines instead.
29+
deprecated since 1.7: pass the first and the last lines instead.
3030

3131
Line-per-line iteration
3232
-----------------------
@@ -66,7 +66,7 @@ $line->getCommit(); // The commit that last changed it
6666
$author = $line->getCommit()->getAuthorName();
6767
```
6868

69-
`Line::getLine()` is deprecated since 2.0: use `getOriginalNumber()`, which
69+
`Line::getLine()` is deprecated since 1.7: use `getOriginalNumber()`, which
7070
returns an integer.
7171

7272
Group reading by commit
@@ -87,4 +87,4 @@ foreach ($blame->getHunks() as $hunk) {
8787
}
8888
```
8989

90-
`getGroupedLines()` is deprecated since 2.0: use `getHunks()`.
90+
`getGroupedLines()` is deprecated since 1.7: use `getHunks()`.

‎doc/branch.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ $branch = $branch->moveTo('master~2');
4141
```
4242

4343
Like `git update-ref`, it doesn't touch the working tree, even for the
44-
branch checked out. `ReferenceBag::update()` is deprecated since 2.0: use
44+
branch checked out. `ReferenceBag::update()` is deprecated since 1.7: use
4545
`moveTo()`.
4646

4747
Merged branches
@@ -56,7 +56,7 @@ if ($branch->isMergedInto('master')) {
5656
}
5757
```
5858

59-
`isMergedTo()` is deprecated since 2.0: use `isMergedInto()`, which has no
59+
`isMergedTo()` is deprecated since 1.7: use `isMergedInto()`, which has no
6060
default destination (`master`) nor flag to compare remote branches only.
6161

6262
Branches containing a commit

0 commit comments

Comments
 (0)