@@ -12,6 +12,10 @@ call triggers a deprecation (`E_USER_DEPRECATED`, through
1212them. Log the deprecations of your application to find them. The other
1313changes, 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+
1519Requirements
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+
3194Opening 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
295377Trees
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+
558680Data returned
559681-------------
560682
@@ -590,9 +712,20 @@ Data returned
590712Other 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
0 commit comments