Completed
Pull Request — master (#139)
by
unknown
02:03
created

Commit::getDiff()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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;
21
22
use \GitElephant\Command\BranchCommand;
23
use \GitElephant\Command\MainCommand;
24
use \GitElephant\Command\RevListCommand;
25
use \GitElephant\Command\RevParseCommand;
26
use \GitElephant\Command\ShowCommand;
27
use \GitElephant\Objects\Commit\Message;
28
use \GitElephant\Repository;
29
30
/**
31
 * The Commit object represent a commit
32
 *
33
 * @author Matteo Giachino <[email protected]>
34
 */
35
class Commit implements TreeishInterface, \Countable
36
{
37
    /**
38
     * @var \GitElephant\Repository
39
     */
40
    private $repository;
41
42
    /**
43
     * @var string
44
     */
45
    private $ref;
46
47
    /**
48
     * sha
49
     *
50
     * @var string
51
     */
52
    private $sha;
53
54
    /**
55
     * tree
56
     *
57
     * @var string
58
     */
59
    private $tree;
60
61
    /**
62
     * the commit parents
63
     *
64
     * @var array
65
     */
66
    private $parents;
67
68
    /**
69
     * the Author instance for author
70
     *
71
     * @var \GitElephant\Objects\Author
72
     */
73
    private $author;
74
75
    /**
76
     * the Author instance for committer
77
     *
78
     * @var \GitElephant\Objects\Author
79
     */
80
    private $committer;
81
82
    /**
83
     * the Message instance
84
     *
85
     * @var \GitElephant\Objects\Commit\Message
86
     */
87
    private $message;
88
89
    /**
90
     * the date for author
91
     *
92
     * @var \DateTime
93
     */
94
    private $datetimeAuthor;
95
96
    /**
97
     * the date for committer
98
     *
99
     * @var \Datetime
100
     */
101
    private $datetimeCommitter;
102
103
    /**
104
     * Class constructor
105
     *
106
     * @param \GitElephant\Repository $repository the repository
107
     * @param string                  $treeish    a treeish reference
108
     */
109 37
    private function __construct(Repository $repository, $treeish = 'HEAD')
110
    {
111 37
        $this->repository = $repository;
112 37
        $this->ref = $treeish;
113 37
        $this->parents = array();
114 37
    }
115
116
    /**
117
     * factory method to create a commit
118
     *
119
     * @param Repository    $repository repository instance
120
     * @param string        $message    commit message
121
     * @param bool          $stageAll   automatically stage the dirty working tree. Alternatively call stage() on the repo
122
     * @param string|Author $author     override the author for this commit
123
     *
124
     * @throws \RuntimeException
125
     * @throws \Symfony\Component\Process\Exception\LogicException
126
     * @throws \InvalidArgumentException
127
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
128
     * @throws \Symfony\Component\Process\Exception\RuntimeException
129
     * @return Commit
130
     */
131 5
    public static function create(Repository $repository, $message, $stageAll = false, $author = null)
132
    {
133 5
        $repository->getCaller()->execute(MainCommand::getInstance($repository)->commit($message, $stageAll, $author));
134
135 5
        return $repository->getCommit();
136
    }
137
138
    /**
139
     * pick an existing commit
140
     *
141
     * @param Repository              $repository repository
142
     * @param TreeishInterface|string $treeish    treeish
143
     *
144
     * @throws \RuntimeException
145
     * @throws \Symfony\Component\Process\Exception\RuntimeException
146
     * @return Commit
147
     */
148 18
    public static function pick(Repository $repository, $treeish = null)
149
    {
150 18
        $commit = new self($repository, $treeish);
0 ignored issues
show
Bug introduced by
It seems like $treeish defined by parameter $treeish on line 148 can also be of type null or object<GitElephant\Objects\TreeishInterface>; however, GitElephant\Objects\Commit::__construct() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
151 18
        $commit->createFromCommand();
152
153 18
        return $commit;
154
    }
155
156
    /**
157
     * static generator to generate a single commit from output of command.show service
158
     *
159
     * @param \GitElephant\Repository $repository  repository
160
     * @param array                   $outputLines output lines
161
     *
162
     * @return Commit
163
     */
164 20
    public static function createFromOutputLines(Repository $repository, $outputLines)
165
    {
166 20
        $commit = new self($repository);
167 20
        $commit->parseOutputLines($outputLines);
168
169 20
        return $commit;
170
    }
171
172
    /**
173
     * get the commit properties from command
174
     *
175
     * @see ShowCommand::commitInfo
176
     */
177 18
    public function createFromCommand()
178
    {
179 18
        $command = ShowCommand::getInstance($this->getRepository())->showCommit($this->ref);
180 18
        $outputLines = $this->getCaller()->execute($command, true, $this->getRepository()->getPath())->getOutputLines();
181 18
        $this->parseOutputLines($outputLines);
182 18
    }
183
184
    /**
185
     * get the branches this commit is contained in
186
     *
187
     * @see BranchCommand::contains
188
     */
189
    public function getContainedIn()
190
    {
191
        $command = BranchCommand::getInstance($this->getRepository())->contains($this->getSha());
192
193
        return array_map('trim', (array)$this->getCaller()->execute($command)->getOutputLines(true));
194
    }
195
196
    /**
197
     * number of commits that lead to this one
198
     *
199
     * @throws \RuntimeException
200
     * @throws \Symfony\Component\Process\Exception\LogicException
201
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
202
     * @throws \Symfony\Component\Process\Exception\RuntimeException
203
     * @return int|void
204
     */
205 3
    public function count()
206
    {
207 3
        $command = RevListCommand::getInstance($this->getRepository())->commitPath($this);
208
209 3
        return count($this->getCaller()->execute($command)->getOutputLines(true));
210
    }
211
212 1
    public function getDiff()
213
    {
214 1
        return $this->getRepository()->getDiff($this);
215
    }
216
217
    /**
218
     * parse the output of a git command showing a commit
219
     *
220
     * @param array $outputLines output lines
221
     */
222 37
    private function parseOutputLines($outputLines)
223
    {
224 37
        $message = array();
225 37
        foreach ($outputLines as $line) {
226 37
            $matches = array();
227 37
            if (preg_match('/^commit (\w+)$/', $line, $matches) > 0) {
228 37
                $this->sha = $matches[1];
229
            }
230 37
            if (preg_match('/^tree (\w+)$/', $line, $matches) > 0) {
231 37
                $this->tree = $matches[1];
232
            }
233 37
            if (preg_match('/^parent (\w+)$/', $line, $matches) > 0) {
234 26
                $this->parents[] = $matches[1];
235
            }
236 37
            if (preg_match('/^author (.*) <(.*)> (\d+) (.*)$/', $line, $matches) > 0) {
237 37
                $author = new Author();
238 37
                $author->setName($matches[1]);
239 37
                $author->setEmail($matches[2]);
240 37
                $this->author = $author;
241 37
                $date = \DateTime::createFromFormat('U O', $matches[3] . ' ' . $matches[4]);
242 37
                $date->modify($date->getOffset() . ' seconds');
243 37
                $this->datetimeAuthor = $date;
0 ignored issues
show
Documentation Bug introduced by
It seems like $date can also be of type false. However, the property $datetimeAuthor is declared as type object<DateTime>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
244
            }
245 37
            if (preg_match('/^committer (.*) <(.*)> (\d+) (.*)$/', $line, $matches) > 0) {
246 37
                $committer = new Author();
247 37
                $committer->setName($matches[1]);
248 37
                $committer->setEmail($matches[2]);
249 37
                $this->committer = $committer;
250 37
                $date = \DateTime::createFromFormat('U O', $matches[3] . ' ' . $matches[4]);
251 37
                $date->modify($date->getOffset() . ' seconds');
252 37
                $this->datetimeCommitter = $date;
0 ignored issues
show
Documentation Bug introduced by
It seems like $date can also be of type false. However, the property $datetimeCommitter is declared as type object<DateTime>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
253
            }
254 37
            if (preg_match('/^    (.*)$/', $line, $matches)) {
255 37
                $message[] = $matches[1];
256
            }
257
        }
258 37
        $this->message = new Message($message);
259 37
    }
260
261
    /**
262
     * Returns true if the commit is a root commit. Usually the first of the repository
263
     *
264
     * @return bool
265
     */
266 3
    public function isRoot()
267
    {
268 3
        return count($this->parents) == 0;
269
    }
270
271
    /**
272
     * toString magic method
273
     *
274
     * @return string the sha
275
     */
276 8
    public function __toString()
277
    {
278 8
        return $this->sha;
279
    }
280
281
    /**
282
     * @return \GitElephant\Command\Caller\Caller
283
     */
284 18
    private function getCaller()
285
    {
286 18
        return $this->getRepository()->getCaller();
287
    }
288
289
    /**
290
     * Repository setter
291
     *
292
     * @param \GitElephant\Repository $repository repository variable
293
     */
294
    public function setRepository($repository)
295
    {
296
        $this->repository = $repository;
297
    }
298
299
    /**
300
     * Repository getter
301
     *
302
     * @return \GitElephant\Repository
303
     */
304 18
    public function getRepository()
305
    {
306 18
        return $this->repository;
307
    }
308
309
    /**
310
     * author getter
311
     *
312
     * @return Author
313
     */
314 4
    public function getAuthor()
315
    {
316 4
        return $this->author;
317
    }
318
319
    /**
320
     * committer getter
321
     *
322
     * @return Author
323
     */
324 4
    public function getCommitter()
325
    {
326 4
        return $this->committer;
327
    }
328
329
    /**
330
     * message getter
331
     *
332
     * @return \GitElephant\Objects\Commit\Message
333
     */
334 11
    public function getMessage()
335
    {
336 11
        return $this->message;
337
    }
338
339
    /**
340
     * parent getter
341
     *
342
     * @return mixed
343
     */
344 2
    public function getParents()
345
    {
346 2
        return $this->parents;
347
    }
348
349
    /**
350
     * sha getter
351
     *
352
     * @param bool $short short version
353
     *
354
     * @return mixed
355
     */
356 23
    public function getSha($short = false)
357
    {
358 23
        return $short ? substr($this->sha, 0, 7) : $this->sha;
359
    }
360
361
    /**
362
     * tree getter
363
     *
364
     * @return mixed
365
     */
366 3
    public function getTree()
367
    {
368 3
        return $this->tree;
369
    }
370
371
    /**
372
     * datetimeAuthor getter
373
     *
374
     * @return mixed
375
     */
376 4
    public function getDatetimeAuthor()
377
    {
378 4
        return $this->datetimeAuthor;
379
    }
380
381
    /**
382
     * datetimeCommitter getter
383
     *
384
     * @return \DateTime
385
     */
386 4
    public function getDatetimeCommitter()
387
    {
388 4
        return $this->datetimeCommitter;
389
    }
390
391
    /**
392
     * rev-parse command - often used to return a commit tag.
393
     *
394
     * @param array         $options the options to apply to rev-parse
395
     *
396
     * @throws \RuntimeException
397
     * @throws \InvalidArgumentException
398
     * @throws \Symfony\Component\Process\Exception\RuntimeException
399
     * @return array
400
     */
401 1
    public function revParse(Array $options = array())
0 ignored issues
show
Coding Style introduced by
As per coding-style, PHP keywords should be in lowercase; expected array, but found Array.
Loading history...
402
    {
403 1
        $c = RevParseCommand::getInstance()->revParse($this, $options);
404 1
        $caller = $this->repository->getCaller();
405 1
        $caller->execute($c);
406
407 1
        return array_map('trim', $caller->getOutputLines(true));
408
    }
409
410
    /**
411
     * Is the commit tagged?
412
     *
413
     * return true if some tag of repository point to this commit
414
     * return false otherwise
415
     *
416
     * @return bool
417
     */
418 2
    public function tagged()
419
    {
420 2
        $result = false;
421 2
        foreach ($this->repository->getTags() as $tag) {
422 1
            if ($tag->getSha() == $this->getSha()) {
423 1
                $result = true;
424 1
                break;
425
            }
426
        }
427
428 2
        return $result;
429
    }
430
431
    /**
432
     * Return Tags that point to this commit
433
     *
434
     * @return Tag[]
435
     */
436 1
    public function getTags()
437
    {
438 1
        $currentCommitTags = array();
439 1
        foreach ($this->repository->getTags() as $tag) {
440 1
            if ($tag->getSha() == $this->getSha()) {
441 1
                $currentCommitTags[] = $tag;
442
            }
443
        }
444
445 1
        return $currentCommitTags;
446
    }
447
}
448