1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* GitElephant - An abstraction layer for git written in PHP |
4
|
|
|
* Copyright (C) 2013 Matteo Giachino |
5
|
|
|
* |
6
|
|
|
* This program is free software: you can redistribute it and/or modify |
7
|
|
|
* it under the terms of the GNU General Public License as published by |
8
|
|
|
* the Free Software Foundation, either version 3 of the License, or |
9
|
|
|
* (at your option) any later version. |
10
|
|
|
* |
11
|
|
|
* This program is distributed in the hope that it will be useful, |
12
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14
|
|
|
* GNU General Public License for more details. |
15
|
|
|
* |
16
|
|
|
* You should have received a copy of the GNU General Public License |
17
|
|
|
* along with this program. If not, see [http://www.gnu.org/licenses/]. |
18
|
|
|
*/ |
19
|
|
|
|
20
|
|
|
namespace GitElephant\Objects\Diff; |
21
|
|
|
|
22
|
|
|
use \GitElephant\Utilities; |
23
|
|
|
use \GitElephant\Repository; |
24
|
|
|
use \GitElephant\Command\DiffTreeCommand; |
25
|
|
|
use \GitElephant\Command\DiffCommand; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Represent a collection of diffs between two trees |
29
|
|
|
* |
30
|
|
|
* @author Matteo Giachino <[email protected]> |
31
|
|
|
*/ |
32
|
|
|
class Diff implements \ArrayAccess, \Countable, \Iterator |
33
|
|
|
{ |
34
|
|
|
/** |
35
|
|
|
* @var \GitElephant\Repository |
36
|
|
|
*/ |
37
|
|
|
private $repository; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* the cursor position |
41
|
|
|
* |
42
|
|
|
* @var int |
43
|
|
|
*/ |
44
|
|
|
private $position; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* DiffObject instances |
48
|
|
|
* |
49
|
|
|
* @var array |
50
|
|
|
*/ |
51
|
|
|
private $diffObjects; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* static generator to generate a Diff object |
55
|
|
|
* |
56
|
|
|
* @param \GitElephant\Repository $repository repository |
57
|
|
|
* @param null|string|\GitElephant\Objects\Commit $commit1 first commit |
58
|
|
|
* @param null|string|\GitElephant\Objects\Commit $commit2 second commit |
59
|
|
|
* @param null|string $path path to consider |
60
|
|
|
* |
61
|
|
|
* @throws \RuntimeException |
62
|
|
|
* @throws \InvalidArgumentException |
63
|
|
|
* @throws \Symfony\Component\Process\Exception\RuntimeException |
64
|
|
|
* @return Diff |
65
|
|
|
*/ |
66
|
2 |
|
public static function create(Repository $repository, $commit1 = null, $commit2 = null, $path = null) |
67
|
|
|
{ |
68
|
2 |
|
$commit = new self($repository); |
69
|
2 |
|
$commit->createFromCommand($commit1, $commit2, $path); |
|
|
|
|
70
|
|
|
|
71
|
2 |
|
return $commit; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Class constructor |
76
|
|
|
* bare Diff object |
77
|
|
|
* |
78
|
|
|
* @param \GitElephant\Repository $repository repository instance |
79
|
|
|
* @param null $diffObjects diff objects |
80
|
|
|
*/ |
81
|
2 |
|
public function __construct(Repository $repository, $diffObjects = null) |
82
|
|
|
{ |
83
|
2 |
|
$this->position = 0; |
84
|
2 |
|
$this->repository = $repository; |
85
|
2 |
|
$this->diffObjects = $diffObjects; |
|
|
|
|
86
|
2 |
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* get the commit properties from command |
90
|
|
|
* |
91
|
|
|
* @param null $commit1 commit 1 |
92
|
|
|
* @param null $commit2 commit 2 |
93
|
|
|
* @param null $path path |
94
|
|
|
* |
95
|
|
|
* @throws \RuntimeException |
96
|
|
|
* @throws \Symfony\Component\Process\Exception\InvalidArgumentException |
97
|
|
|
* @throws \Symfony\Component\Process\Exception\LogicException |
98
|
|
|
* @throws \InvalidArgumentException |
99
|
|
|
* @throws \Symfony\Component\Process\Exception\RuntimeException |
100
|
|
|
* @see ShowCommand::commitInfo |
101
|
|
|
*/ |
102
|
2 |
|
public function createFromCommand($commit1 = null, $commit2 = null, $path = null) |
103
|
|
|
{ |
104
|
2 |
|
if (null === $commit1) { |
105
|
|
|
$commit1 = $this->getRepository()->getCommit(); |
106
|
|
|
} |
107
|
2 |
|
if (is_string($commit1)) { |
108
|
|
|
$commit1 = $this->getRepository()->getCommit($commit1); |
109
|
|
|
} |
110
|
2 |
|
if ($commit2 === null) { |
111
|
2 |
|
if ($commit1->isRoot()) { |
|
|
|
|
112
|
|
|
$command = DiffTreeCommand::getInstance($this->repository)->rootDiff($commit1); |
|
|
|
|
113
|
|
|
} else { |
114
|
2 |
|
$command = DiffCommand::getInstance($this->repository)->diff($commit1); |
|
|
|
|
115
|
|
|
} |
116
|
2 |
|
} else { |
117
|
|
|
if (is_string($commit2)) { |
118
|
|
|
$commit2 = $this->getRepository()->getCommit($commit2); |
119
|
|
|
} |
120
|
|
|
$command = DiffCommand::getInstance($this->repository)->diff($commit1, $commit2, $path); |
|
|
|
|
121
|
|
|
} |
122
|
2 |
|
$outputLines = $this->getCaller()->execute($command)->getOutputLines(); |
123
|
2 |
|
$this->parseOutputLines($outputLines); |
124
|
2 |
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* parse the output of a git command showing a commit |
128
|
|
|
* |
129
|
|
|
* @param array $outputLines output lines |
130
|
|
|
* |
131
|
|
|
* @throws \InvalidArgumentException |
132
|
|
|
*/ |
133
|
2 |
|
private function parseOutputLines($outputLines) |
134
|
|
|
{ |
135
|
2 |
|
$this->diffObjects = array(); |
136
|
2 |
|
$splitArray = Utilities::pregSplitArray($outputLines, '/^diff --git SRC\/(.*) DST\/(.*)$/'); |
137
|
2 |
|
foreach ($splitArray as $diffObjectLines) { |
138
|
2 |
|
$this->diffObjects[] = new DiffObject($diffObjectLines); |
139
|
2 |
|
} |
140
|
2 |
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return \GitElephant\Command\Caller\Caller |
144
|
|
|
*/ |
145
|
2 |
|
private function getCaller() |
146
|
|
|
{ |
147
|
2 |
|
return $this->getRepository()->getCaller(); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Repository setter |
152
|
|
|
* |
153
|
|
|
* @param \GitElephant\Repository $repository the repository variable |
154
|
|
|
*/ |
155
|
|
|
public function setRepository($repository) |
156
|
|
|
{ |
157
|
|
|
$this->repository = $repository; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Repository getter |
162
|
|
|
* |
163
|
|
|
* @return \GitElephant\Repository |
164
|
|
|
*/ |
165
|
2 |
|
public function getRepository() |
166
|
|
|
{ |
167
|
2 |
|
return $this->repository; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* ArrayAccess interface |
172
|
|
|
* |
173
|
|
|
* @param int $offset offset |
174
|
|
|
* |
175
|
|
|
* @return bool |
176
|
|
|
*/ |
177
|
|
|
public function offsetExists($offset) |
178
|
|
|
{ |
179
|
|
|
return isset($this->diffObjects[$offset]); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* ArrayAccess interface |
184
|
|
|
* |
185
|
|
|
* @param int $offset offset |
186
|
|
|
* |
187
|
|
|
* @return null|mixed |
188
|
|
|
*/ |
189
|
1 |
|
public function offsetGet($offset) |
190
|
|
|
{ |
191
|
1 |
|
return isset($this->diffObjects[$offset]) ? $this->diffObjects[$offset] : null; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* ArrayAccess interface |
196
|
|
|
* |
197
|
|
|
* @param int $offset offset |
198
|
|
|
* @param mixed $value value |
199
|
|
|
*/ |
200
|
|
|
public function offsetSet($offset, $value) |
201
|
|
|
{ |
202
|
|
|
if (is_null($offset)) { |
203
|
|
|
$this->diffObjects[] = $value; |
204
|
|
|
} else { |
205
|
|
|
$this->diffObjects[$offset] = $value; |
206
|
|
|
} |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* ArrayAccess interface |
211
|
|
|
* |
212
|
|
|
* @param int $offset offset |
213
|
|
|
*/ |
214
|
|
|
public function offsetUnset($offset) |
215
|
|
|
{ |
216
|
|
|
unset($this->diffObjects[$offset]); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Countable interface |
221
|
|
|
* |
222
|
|
|
* @return int|void |
223
|
|
|
*/ |
224
|
2 |
|
public function count() |
225
|
|
|
{ |
226
|
2 |
|
return count($this->diffObjects); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Iterator interface |
231
|
|
|
* |
232
|
|
|
* @return mixed |
233
|
|
|
*/ |
234
|
|
|
public function current() |
235
|
|
|
{ |
236
|
|
|
return $this->diffObjects[$this->position]; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Iterator interface |
241
|
|
|
*/ |
242
|
|
|
public function next() |
243
|
|
|
{ |
244
|
|
|
++$this->position; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Iterator interface |
249
|
|
|
* |
250
|
|
|
* @return int |
251
|
|
|
*/ |
252
|
|
|
public function key() |
253
|
|
|
{ |
254
|
|
|
return $this->position; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Iterator interface |
259
|
|
|
* |
260
|
|
|
* @return bool |
261
|
|
|
*/ |
262
|
|
|
public function valid() |
263
|
|
|
{ |
264
|
|
|
return isset($this->diffObjects[$this->position]); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Iterator interface |
269
|
|
|
*/ |
270
|
|
|
public function rewind() |
271
|
|
|
{ |
272
|
|
|
$this->position = 0; |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.