Skip to content

Commit 1aaa8df

Browse files
committed
Refactor git wrapper out of GitMerge #8
1 parent 8191a36 commit 1aaa8df

10 files changed

Lines changed: 450 additions & 43 deletions

File tree

‎README.md‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ PhpMerge is a small library that solves this problem. There are two classes:
2121

2222
`PhpMerge` uses `SebastianBergmann\Diff\Differ` to get the differences between
2323
the different versions and calculates the merged text from it.
24-
`GitMerge` uses `Symplify\GitWrapper\GitWrapper`, writes the text to a temporary file
25-
and uses the command line git to merge the text.
24+
`GitMerge` uses `Gitonomy\Git\Repository` or `Symplify\GitWrapper\GitWrapper`,
25+
writes the text to a temporary file and uses the command line git to branch,
26+
add and merge the text. Using GitMerge is not recommended for performance
27+
reasons, but if you want to merge with git then this is the simplest way.
2628

2729
## Usage
2830

@@ -116,8 +118,8 @@ try {
116118
$conflicting_lines = $conflicts[0]->getLocal();
117119
// $conflicting_lines === ["normal!!\n"];
118120

119-
$line_numer_of_conflict = $conflicts[0]->getBaseLine();
120-
// $line_numer_of_conflict === 3; // Count starts with 0.
121+
$line_number_of_conflict = $conflicts[0]->getBaseLine();
122+
// $line_number_of_conflict === 3; // Count starts with 0.
121123

122124
// It is also possible to get the merged version using the first version
123125
// to resolve conflicts.
@@ -157,6 +159,16 @@ the library as a dependency to your composer.json file.
157159

158160
To use the command line git with `GitMerge`:
159161

162+
```json
163+
{
164+
"require": {
165+
"bircher/php-merge": "~4.0",
166+
"gitonomy/gitlib": "^1.3"
167+
}
168+
}
169+
```
170+
171+
or with the deprecated symplify/git-wrapper
160172
```json
161173
{
162174
"require": {
@@ -172,5 +184,6 @@ for installation and usage instructions.
172184

173185
## Difference to ~3.0
174186

175-
In the ~4.0 version we switch from `cpliakas/git-wrapper` to `symplify/git-wrapper` since the former is deprecated.
187+
In the ~4.0 version we switch from `cpliakas/git-wrapper`
188+
to `symplify/git-wrapper` or `gitonomy/gitlib` since the former is deprecated.
176189
This update means that there is no change when only using `PhpMerge\PhpMerge`.

‎composer.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"sebastian/diff": "^2.0|^3.0|^4.0|^5.0|^6.0|^7.0"
2121
},
2222
"require-dev": {
23+
"gitonomy/gitlib": "^1.5",
2324
"symplify/git-wrapper": "^9.1|^10.0",
2425
"phpunit/phpunit": "~6|~7|~8|~9|~10|~11|~12",
2526
"squizlabs/php_codesniffer": "~4",

‎src/PhpMerge/GitMerge.php‎

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212

1313
namespace PhpMerge;
1414

15-
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
16-
use Symplify\GitWrapper\GitWrapper;
17-
use Symplify\GitWrapper\Exception\GitException;
15+
use Gitonomy\Git\Repository;
16+
use PhpMerge\internal\AbstractMergeBase;
17+
use PhpMerge\internal\Git\Gitonomy;
18+
use PhpMerge\internal\Git\GitAbstractionInterface;
19+
use PhpMerge\internal\Git\GitException;
20+
use PhpMerge\internal\Git\Symplify;
1821
use PhpMerge\internal\Line;
1922
use PhpMerge\internal\Hunk;
20-
use PhpMerge\internal\AbstractMergeBase;
23+
use Symplify\GitWrapper\GitWrapper;
2124
use SebastianBergmann\Diff\Differ;
25+
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
2226

2327
/**
2428
* Class GitMerge merges three strings with git as the backend.
@@ -35,16 +39,9 @@ final class GitMerge extends AbstractMergeBase implements PhpMergeInterface
3539
/**
3640
* The git working directory.
3741
*
38-
* @var \Symplify\GitWrapper\GitWorkingCopy|null
42+
* @var GitAbstractionInterface
3943
*/
40-
protected $git;
41-
42-
/**
43-
* The git wrapper to use for merging.
44-
*
45-
* @var \Symplify\GitWrapper\GitWrapper
46-
*/
47-
protected $wrapper;
44+
protected GitAbstractionInterface $git;
4845

4946
/**
5047
* The temporary directory in which git can work.
@@ -61,17 +58,29 @@ final class GitMerge extends AbstractMergeBase implements PhpMergeInterface
6158
/**
6259
* Constructor, not setting anything up.
6360
*
64-
* @param \Symplify\GitWrapper\GitWrapper|null $wrapper
61+
* @param GitAbstractionInterface|\Symplify\GitWrapper\GitWrapper|null $wrapper
6562
*/
66-
public function __construct(?GitWrapper $wrapper = null)
63+
public function __construct($wrapper = null)
6764
{
68-
if (!$wrapper) {
69-
$wrapper = new GitWrapper('git');
70-
}
71-
$this->wrapper = $wrapper;
7265
$this->conflict = '';
73-
$this->git = null;
7466
$this->dir = null;
67+
68+
// Set up the abstraction layer.
69+
if ($wrapper instanceof GitAbstractionInterface) {
70+
// This allows the user to choose.
71+
$this->git = $wrapper;
72+
} elseif ($wrapper instanceof GitWrapper) {
73+
// This is the backwards compatibility with an argument.
74+
$this->git = new Symplify($wrapper);
75+
} elseif (class_exists(Repository::class)) {
76+
// We use this if no wrapper is passed and the newer library exists.
77+
$this->git = new Gitonomy();
78+
} elseif (class_exists(GitWrapper::class)) {
79+
// This is the backwards compatibility when only the deprecated class exists.
80+
$this->git = new Symplify(new GitWrapper('git'));
81+
} else {
82+
throw new \InvalidArgumentException('No git wrapper library found. use composer to install gitonomy/gitlib or symplify/git-wrapper');
83+
}
7584
}
7685

7786
/**
@@ -135,7 +144,7 @@ protected function mergeFile(string $file, string $base, string $remote, string
135144
$this->git->add($file);
136145
$this->git->commit('Add base.');
137146

138-
if (!in_array('original', $this->git->getBranches()->all())) {
147+
if (!$this->git->branchExists('original')) {
139148
$this->git->checkoutNewBranch('original');
140149
} else {
141150
$this->git->checkout('original');
@@ -356,19 +365,14 @@ protected static function fixLastLine(array $lines, array $all): array
356365
protected function setup()
357366
{
358367
if (!$this->dir) {
359-
// Greate a temporary directory.
368+
// Create a temporary directory.
360369
$tempfile = tempnam(sys_get_temp_dir(), '');
361370
mkdir($tempfile.'.git');
362371
if (file_exists($tempfile)) {
363372
unlink($tempfile);
364373
}
365374
$this->dir = $tempfile.'.git';
366-
$this->git = $this->wrapper->init($this->dir);
367-
}
368-
if ($this->git) {
369-
$this->git->config('user.name', 'GitMerge');
370-
$this->git->config('user.email', 'gitmerge@php-merge.example.com');
371-
$this->git->config('merge.conflictStyle', 'diff3');
375+
$this->git->init($this->dir);
372376
}
373377
}
374378

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
/**
3+
* This file is part of the php-merge package.
4+
*
5+
* (c) Fabian Bircher <opensource@fabianbircher.com>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace PhpMerge\internal\Git;
12+
13+
/**
14+
* The interface of git commands we need.
15+
*
16+
* This is an internal implementation detail so that we can more easily
17+
* support different libraries wrapping around the git executable.
18+
*/
19+
interface GitAbstractionInterface
20+
{
21+
/**
22+
* Sets up the git repository in the given directory.
23+
*
24+
* This method is also responsible for setting the git config.
25+
* For example the merge.conflictStyle needs to be set to diff3.
26+
*
27+
* @param string $directory
28+
* The empty directory to set up git in.
29+
*/
30+
public function init(string $directory): void;
31+
32+
/**
33+
* Add a path to the stage in git.
34+
*
35+
* @param string $path
36+
* The path to add.
37+
*/
38+
public function add(string $path): void;
39+
40+
/**
41+
* Commit the changes to git.
42+
*
43+
* @param string $message
44+
* The commit message
45+
*/
46+
public function commit(string $message): void;
47+
48+
49+
/**
50+
* Checks if a branch exists.
51+
*
52+
* @param string $branch
53+
* The branch name.
54+
* @return bool
55+
* True if it exists.
56+
*/
57+
public function branchExists(string $branch): bool;
58+
59+
/**
60+
* Checkout a given branch.
61+
*
62+
* @param string $branch
63+
*/
64+
public function checkout(string $branch): void;
65+
66+
/**
67+
* Checkout a new branch with a given name.
68+
*
69+
* @param string $branch
70+
*/
71+
public function checkoutNewBranch(string $branch): void;
72+
73+
/**
74+
* Rebase onto a branch.
75+
*
76+
* @param string $branch
77+
* The branch to rebase onto.
78+
*/
79+
public function rebase(string $branch): void;
80+
81+
82+
/**
83+
* Merge a branch.
84+
*
85+
* @param string $branch
86+
* The branch to merge.
87+
*
88+
* @throws GitException
89+
* Thrown when there is a merge conflict.
90+
*/
91+
public function merge(string $branch): void;
92+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* This file is part of the php-merge package.
4+
*
5+
* (c) Fabian Bircher <opensource@fabianbircher.com>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace PhpMerge\internal\Git;
14+
15+
use RuntimeException;
16+
17+
/**
18+
* The exception thrown when a merge conflict happens.
19+
*/
20+
final class GitException extends RuntimeException
21+
{
22+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
/**
3+
* This file is part of the php-merge package.
4+
*
5+
* (c) Fabian Bircher <opensource@fabianbircher.com>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace PhpMerge\internal\Git;
12+
13+
use Gitonomy\Git\Admin;
14+
use Gitonomy\Git\Exception\RuntimeException;
15+
use Gitonomy\Git\Repository;
16+
17+
/**
18+
* Our abstraction layer for the optional gitonomy/gitlib library.
19+
*/
20+
class Gitonomy implements GitAbstractionInterface
21+
{
22+
protected ?Repository $repository;
23+
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function init(string $directory): void
28+
{
29+
$this->repository = Admin::init($directory, false);
30+
31+
$this->repository->run('config', ['user.name', 'GitMerge']);
32+
$this->repository->run('config', ['user.email', 'gitmerge@php-merge.example.com']);
33+
$this->repository->run('config', ['merge.conflictStyle', 'diff3']);
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function add(string $path): void
40+
{
41+
$this->repository->run('add', [$path]);
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function commit(string $message): void
48+
{
49+
$this->repository->run('commit', ['-m', $message]);
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
public function branchExists(string $branch): bool
56+
{
57+
return $this->repository->getReferences(true)->hasBranch($branch);
58+
}
59+
60+
/**
61+
* {@inheritdoc}
62+
*/
63+
public function checkout(string $branch): void
64+
{
65+
$this->repository->getWorkingCopy()->checkout($branch);
66+
}
67+
68+
/**
69+
* {@inheritdoc}
70+
*/
71+
public function checkoutNewBranch(string $branch): void
72+
{
73+
$this->repository->getWorkingCopy()->checkout($this->repository->getHead(), $branch);
74+
}
75+
76+
/**
77+
* {@inheritdoc}
78+
*/
79+
public function rebase(string $branch): void
80+
{
81+
$this->repository->run('rebase', [$branch]);
82+
}
83+
84+
/**
85+
* {@inheritdoc}
86+
*/
87+
public function merge(string $branch): void
88+
{
89+
try {
90+
$this->repository->run('merge', [$branch]);
91+
} catch (RuntimeException $e) {
92+
throw new GitException($e->getMessage(), $e->getCode(), $e);
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)