Completed
Push — develop ( 61d4f1...4b8b90 )
by
unknown
9s
created

Repository::merge()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4.0058

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 13
cts 14
cp 0.9286
rs 9.52
c 0
b 0
f 0
cc 4
nc 4
nop 3
crap 4.0058
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;
21
22
use GitElephant\Command\ResetCommand;
23
use GitElephant\Command\StashCommand;
24
use GitElephant\Objects\TreeObject;
25
use Symfony\Component\Process\Exception\InvalidArgumentException;
26
use \GitElephant\Command\BranchCommand;
27
use \GitElephant\Command\Caller\Caller;
28
use \GitElephant\Command\CatFileCommand;
29
use \GitElephant\Command\CloneCommand;
30
use \GitElephant\Command\FetchCommand;
31
use \GitElephant\Command\LogCommand;
32
use \GitElephant\Command\LsTreeCommand;
33
use \GitElephant\Command\MainCommand;
34
use \GitElephant\Command\MergeCommand;
35
use \GitElephant\Command\PullCommand;
36
use \GitElephant\Command\PushCommand;
37
use \GitElephant\Command\RemoteCommand;
38
use \GitElephant\Command\RevParseCommand;
39
use \GitElephant\Command\SubmoduleCommand;
40
use \GitElephant\Command\TagCommand;
41
use \GitElephant\Objects\Author;
42
use \GitElephant\Objects\Branch;
43
use \GitElephant\Objects\Commit;
44
use \GitElephant\Objects\Diff\Diff;
45
use \GitElephant\Objects\Log;
46
use \GitElephant\Objects\LogRange;
47
use \GitElephant\Objects\NodeObject;
48
use \GitElephant\Objects\Remote;
49
use \GitElephant\Objects\Tag;
50
use \GitElephant\Objects\Tree;
51
use \GitElephant\Objects\TreeishInterface;
52
use \GitElephant\Status\Status;
53
use \GitElephant\Status\StatusIndex;
54
use \GitElephant\Status\StatusWorkingTree;
55
use \Symfony\Component\Filesystem\Filesystem;
56
use \Symfony\Component\Finder\Finder;
57
use \Symfony\Component\Finder\SplFileInfo;
58
59
/**
60
 * Repository
61
 *
62
 * Base Class for repository operations
63
 *
64
 * @author Matteo Giachino <[email protected]>
65
 * @author Dhaval Patel <[email protected]>
66
 * @author Kirk Madera <[email protected]>
67
 */
68
class Repository
69
{
70
    /**
71
     * the repository path
72
     *
73
     * @var string
74
     */
75
    private $path;
76
77
    /**
78
     * the caller instance
79
     *
80
     * @var \GitElephant\Command\Caller\Caller
81
     */
82
    private $caller;
83
84
    /**
85
     * A general repository name
86
     *
87
     * @var string $name the repository name
88
     */
89
    private $name;
90
91
    /**
92
     * A list of global configs to apply to every command
93
     *
94
     * @var array
95
     */
96
    private $globalConfigs = [];
97
98
    /**
99
     * A list of global options to apply to every command
100
     *
101
     * @var array
102
     */
103
    private $globalOptions = [];
104
105
    /**
106
     * A list of global arguments to apply to every command
107
     *
108
     * @var array
109
     */
110
    private $globalCommandArguments = [];
111
112
    /**
113
     * Class constructor
114
     *
115
     * @param string         $repositoryPath the path of the git repository
116
     * @param string|null $binary         the path to the git binary
117
     * @param string         $name           a repository name
118
     *
119
     * @throws Exception\InvalidRepositoryPathException
120
     */
121 107
    public function __construct($repositoryPath, string $binary = null, $name = null)
122
    {
123 107
        $this->path = $repositoryPath;
124 107
        $this->caller = new Caller($binary, $repositoryPath);
125 107
        $this->name = $name;
126 107
    }
127
128
    /**
129
     * Factory method
130
     *
131
     * @param string         $repositoryPath the path of the git repository
132
     * @param string|null $binary         the path to the git binary
133
     * @param string         $name           a repository name
134
     *
135
     * @return \GitElephant\Repository
136
     */
137 106
    public static function open($repositoryPath, string $binary = null, $name = null)
138
    {
139 106
        return new self($repositoryPath, $binary, $name);
140
    }
141
142
    /**
143
     * create a repository from a remote git url, or a local filesystem
144
     * and save it in a temp folder
145
     *
146
     * @param string|Repository $git            the git remote url, or the filesystem path
147
     * @param null              $repositoryPath path
148
     * @param string|null $binary         the path to the git binary
149
     * @param null              $name           repository name
150
     *
151
     * @throws \RuntimeException
152
     * @throws \Symfony\Component\Filesystem\Exception\IOException
153
     * @return Repository
154
     */
155 1
    public static function createFromRemote($git, $repositoryPath = null, string $binary = null, $name = null)
156
    {
157 1
        if (null === $repositoryPath) {
158 1
            $tempDir = realpath(sys_get_temp_dir());
159 1
            $repositoryPath = sprintf('%s%s%s', $tempDir, DIRECTORY_SEPARATOR, sha1(uniqid()));
160 1
            $fs = new Filesystem();
161 1
            $fs->mkdir($repositoryPath);
162
        }
163 1
        $repository = new Repository($repositoryPath, $binary, $name);
164 1
        if ($git instanceof Repository) {
165
            $git = $git->getPath();
166
        }
167 1
        $repository->cloneFrom($git, $repositoryPath);
168 1
        $repository->checkoutAllRemoteBranches();
169
170 1
        return $repository;
171
    }
172
173
    /**
174
     * Init the repository
175
     *
176
     * @param bool $bare created a bare repository
177
     *
178
     * @throws \RuntimeException
179
     * @throws \Symfony\Component\Process\Exception\LogicException
180
     * @throws InvalidArgumentException
181
     * @throws \Symfony\Component\Process\Exception\RuntimeException
182
     * @return Repository
183
     */
184 94
    public function init($bare = false)
185
    {
186 94
        $this->caller->execute(MainCommand::getInstance($this)->init($bare));
187
188 94
        return $this;
189
    }
190
191
    /**
192
     * Stage the working tree content
193
     *
194
     * @param string|NodeObject $path the path to store
195
     *
196
     * @throws \RuntimeException
197
     * @throws \Symfony\Component\Process\Exception\LogicException
198
     * @throws InvalidArgumentException
199
     * @throws \Symfony\Component\Process\Exception\RuntimeException
200
     * @return Repository
201
     */
202 90
    public function stage($path = '.')
203
    {
204 90
        $this->caller->execute(MainCommand::getInstance($this)->add($path));
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 202 can also be of type object<GitElephant\Objects\NodeObject>; however, GitElephant\Command\MainCommand::add() 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...
205
206 90
        return $this;
207
    }
208
209
    /**
210
     * Unstage a tree content
211
     *
212
     * @param string|NodeObject $path the path to unstage
213
     *
214
     * @throws \RuntimeException
215
     * @throws \Symfony\Component\Process\Exception\LogicException
216
     * @throws InvalidArgumentException
217
     * @throws \Symfony\Component\Process\Exception\RuntimeException
218
     * @return Repository
219
     */
220 2
    public function unstage($path)
221
    {
222 2
        $this->caller->execute(MainCommand::getInstance($this)->unstage($path), true, null, [0, 1]);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 220 can also be of type object<GitElephant\Objects\NodeObject>; however, GitElephant\Command\MainCommand::unstage() 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...
223
224 2
        return $this;
225
    }
226
227
    /**
228
     * Move a file/directory
229
     *
230
     * @param string|NodeObject $from source path
231
     * @param string|NodeObject $to   destination path
232
     *
233
     * @throws \RuntimeException
234
     * @throws \Symfony\Component\Process\Exception\LogicException
235
     * @throws \InvalidArgumentException
236
     * @throws InvalidArgumentException
237
     * @throws \Symfony\Component\Process\Exception\RuntimeException
238
     * @return Repository
239
     */
240 1
    public function move($from, $to)
241
    {
242 1
        $this->caller->execute(MainCommand::getInstance($this)->move($from, $to));
243
244 1
        return $this;
245
    }
246
247
    /**
248
     * Remove a file/directory
249
     *
250
     * @param string|NodeObject $path      the path to remove
251
     * @param bool              $recursive recurse
252
     * @param bool              $force     force
253
     *
254
     * @throws \RuntimeException
255
     * @throws \Symfony\Component\Process\Exception\LogicException
256
     * @throws \InvalidArgumentException
257
     * @throws InvalidArgumentException
258
     * @throws \Symfony\Component\Process\Exception\RuntimeException
259
     * @return Repository
260
     */
261 1
    public function remove($path, $recursive = false, $force = false)
262
    {
263 1
        $this->caller->execute(MainCommand::getInstance($this)->remove($path, $recursive, $force));
264
265 1
        return $this;
266
    }
267
268
    /**
269
     * Commit content to the repository, eventually staging all unstaged content
270
     *
271
     * @param string        $message    the commit message
272
     * @param bool          $stageAll   whether to stage on not everything before commit
273
     * @param string|null   $ref        the reference to commit to (checkout -> commit -> checkout previous)
274
     * @param string|Author $author     override the author for this commit
275
     * @param bool          $allowEmpty override the author for this commit
276
     *
277
     * @throws \RuntimeException
278
     * @throws \InvalidArgumentException
279
     * @throws \Symfony\Component\Process\Exception\RuntimeException
280
     * @return Repository
281
     */
282 85
    public function commit(string $message, $stageAll = false, $ref = null, $author = null, $allowEmpty = false)
283
    {
284 85
        $currentBranch = null;
285 85
        if (!is_null($ref)) {
286 1
            $currentBranch = $this->getMainBranch();
287 1
            $this->checkout($ref);
288
        }
289 85
        if ($stageAll) {
290 83
            $this->stage();
291
        }
292 85
        $this->caller->execute(MainCommand::getInstance($this)->commit($message, $stageAll, $author, $allowEmpty));
293 85
        if (!is_null($ref)) {
294 1
            $this->checkout($currentBranch);
0 ignored issues
show
Bug introduced by
It seems like $currentBranch defined by null on line 284 can be null; however, GitElephant\Repository::checkout() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
295
        }
296
297 85
        return $this;
298
    }
299
300
    /**
301
     * rev-parse command - often used to return a commit tag.
302
     *
303
     * @param array                    $options the options to apply to rev-parse
304
     * @param string|NodeObject|Commit $arg     the argument (may be a branch head, etc)
305
     *
306
     * @throws \RuntimeException
307
     * @throws \InvalidArgumentException
308
     * @throws \Symfony\Component\Process\Exception\RuntimeException
309
     * @return array
310
     */
311 1
    public function revParse(string $arg = null, array $options = [])
312
    {
313 1
        $this->caller->execute(RevParseCommand::getInstance()->revParse($arg, $options));
314
315 1
        return array_map('trim', $this->caller->getOutputLines(true));
316
    }
317
318
    /**
319
     * Check if this is a bare repository
320
     *
321
     * @return boolean
322
     */
323 1
    public function isBare()
324
    {
325 1
        $options = [RevParseCommand::OPTION_IS_BARE_REPOSIORY];
326 1
        $this->caller->execute(RevParseCommand::getInstance()->revParse(null, $options));
327
328 1
        return trim($this->caller->getOutput()) === 'true';
329
    }
330
331
    /**
332
     * @param TreeishInterface|Commit|string $arg
333
     * @param array                          $options
334
     */
335 2
    public function reset($arg, $options)
336
    {
337 2
        $this->caller->execute(ResetCommand::getInstance($this)->reset($arg, $options));
338 2
    }
339
340
    /**
341
     * Get the repository status
342
     *
343
     * @return Status
344
     */
345 5
    public function getStatus()
346
    {
347 5
        return Status::get($this);
348
    }
349
350
    /**
351
     * @return Status
352
     */
353 1
    public function getWorkingTreeStatus()
354
    {
355 1
        return StatusWorkingTree::get($this);
356
    }
357
358
    /**
359
     * @return Status
360
     */
361 4
    public function getIndexStatus()
362
    {
363 4
        return StatusIndex::get($this);
364
    }
365
366
    /**
367
     * isClean Return true if the repository is not dirty.
368
     *
369
     * @return boolean
370
     */
371
    public function isClean()
372
    {
373
        return $this->getStatus()->all()->isEmpty();
374
    }
375
376
    /**
377
     * isDirty Return true if the repository has some modified files.
378
     *
379
     * @return boolean
380
     */
381
    public function isDirty()
382
    {
383
        return !$this->isClean();
384
    }
385
386
    /**
387
     * Get the repository status as a string
388
     *
389
     * @throws \RuntimeException
390
     * @throws \Symfony\Component\Process\Exception\LogicException
391
     * @throws InvalidArgumentException
392
     * @throws \Symfony\Component\Process\Exception\RuntimeException
393
     * @return array
394
     */
395 4
    public function getStatusOutput()
396
    {
397 4
        $this->caller->execute(MainCommand::getInstance($this)->status());
398
399 4
        return array_map('trim', $this->caller->getOutputLines());
400
    }
401
402
    /**
403
     * Create a new branch
404
     *
405
     * @param string $name       the new branch name
406
     * @param null   $startPoint the reference to create the branch from
407
     *
408
     * @throws \RuntimeException
409
     * @throws \Symfony\Component\Process\Exception\RuntimeException
410
     * @return Repository
411
     */
412 27
    public function createBranch(string $name, $startPoint = null)
413
    {
414 27
        Branch::create($this, $name, $startPoint);
415
416 27
        return $this;
417
    }
418
419
    /**
420
     * Delete a branch by its name
421
     * This function change the state of the repository on the filesystem
422
     *
423
     * @param string $name  The branch to delete
424
     * @param bool   $force Force the delete
425
     *
426
     * @throws \RuntimeException
427
     * @throws \Symfony\Component\Process\Exception\LogicException
428
     * @throws InvalidArgumentException
429
     * @throws \Symfony\Component\Process\Exception\RuntimeException
430
     * @return Repository
431
     */
432 1
    public function deleteBranch(string $name, bool $force = false)
433
    {
434 1
        $this->caller->execute(BranchCommand::getInstance($this)->delete($name, $force));
435
436 1
        return $this;
437
    }
438
439
    /**
440
     * An array of Branch objects
441
     *
442
     * @param bool $namesOnly return an array of branch names as a string
443
     * @param bool $all       lists also remote branches
444
     *
445
     * @throws \RuntimeException
446
     * @throws InvalidArgumentException
447
     * @throws \Symfony\Component\Process\Exception\LogicException
448
     * @throws \InvalidArgumentException
449
     * @throws \Symfony\Component\Process\Exception\RuntimeException
450
     * @return array
451
     */
452 17
    public function getBranches(bool $namesOnly = false, bool $all = false)
453
    {
454 17
        $branches = [];
455 17
        if ($namesOnly) {
456 6
            $outputLines = $this->caller
457 6
                ->execute(BranchCommand::getInstance($this)->listBranches($all, true))
458 6
                ->getOutputLines(true);
459
460 6
            $branches = array_map(
461
                function ($v) {
462 6
                    return ltrim($v, '* ');
463 6
                },
464 6
                $outputLines
465
            );
466
        } else {
467 14
            $outputLines = $this->caller
468 14
                ->execute(BranchCommand::getInstance($this)->listBranches($all))
469 14
                ->getOutputLines(true);
470
471 14
            foreach ($outputLines as $branchLine) {
472 14
                $branches[] = Branch::createFromOutputLine($this, $branchLine);
473
            }
474
        }
475
476 17
        return $branches;
477
    }
478
479
    /**
480
     * Return the actually checked out branch
481
     *
482
     * @throws \RuntimeException
483
     * @throws \InvalidArgumentException
484
     * @throws \Symfony\Component\Process\Exception\RuntimeException
485
     * @return Objects\Branch
486
     */
487 5
    public function getMainBranch()
488
    {
489 5
        $filtered = array_filter(
490 5
            $this->getBranches(),
491
            function (Branch $branch) {
492 5
                return $branch->getCurrent();
493 5
            }
494
        );
495 5
        sort($filtered);
496
497 5
        return $filtered[0];
498
    }
499
500
    /**
501
     * Retrieve a Branch object by a branch name
502
     *
503
     * @param string $name The branch name
504
     *
505
     * @throws \RuntimeException
506
     * @throws \InvalidArgumentException
507
     * @throws \Symfony\Component\Process\Exception\RuntimeException
508
     * @return null|Branch
509
     */
510 9
    public function getBranch(string $name)
511
    {
512
        /** @var Branch $branch */
513 9
        foreach ($this->getBranches() as $branch) {
514 9
            if ($branch->getName() === $name) {
515 9
                return $branch;
516
            }
517
        }
518
519 1
        return null;
520
    }
521
522
    /**
523
     * Checkout all branches from the remote and make them local
524
     *
525
     * @param string $remote remote to fetch from
526
     *
527
     * @throws \RuntimeException
528
     * @throws \InvalidArgumentException
529
     * @throws \Symfony\Component\Process\Exception\RuntimeException
530
     * @return Repository
531
     */
532 1
    public function checkoutAllRemoteBranches($remote = 'origin')
533
    {
534 1
        $actualBranch = $this->getMainBranch();
535 1
        $actualBranches = $this->getBranches(true, false);
536 1
        $allBranches = $this->getBranches(true, true);
537 1
        $realBranches = array_filter(
538 1
            $allBranches,
539
            function (string $branch) use ($actualBranches) {
540 1
                return !in_array($branch, $actualBranches)
541 1
                && preg_match('/^remotes(.+)$/', $branch)
542 1
                && !preg_match('/^(.+)(HEAD)(.*?)$/', $branch);
543 1
            }
544
        );
545
546 1
        foreach ($realBranches as $realBranch) {
547 1
            $this->checkout(str_replace(sprintf('remotes/%s/', $remote), '', $realBranch));
548
        }
549
550 1
        $this->checkout($actualBranch);
551
552 1
        return $this;
553
    }
554
555
    /**
556
     * Merge a Branch in the current checked out branch
557
     *
558
     * @param Objects\Branch $branch  The branch to merge in the current checked out branch
559
     * @param string         $message The message for the merge commit, if merge is 3-way
560
     * @param string         $mode    The merge mode: ff-only, no-ff or auto
561
     *
562
     * @throws \RuntimeException
563
     * @throws \Symfony\Component\Process\Exception\LogicException
564
     * @throws InvalidArgumentException
565
     * @throws \Symfony\Component\Process\Exception\RuntimeException
566
     * @return Repository
567
     */
568 2
    public function merge(Branch $branch, string $message = '', string $mode = 'auto')
569
    {
570
        $valid_modes = [
571 2
            'auto', // deafult git behavior
572
            'ff-only', // force fast forward merge
573
            'no-ff', // force 3-way merge
574
        ];
575 2
        if (!in_array($mode, $valid_modes)) {
576
            throw new InvalidArgumentException("Invalid merge mode: $mode.");
577
        }
578
579 2
        $options = [];
580 2
        switch ($mode) {
581 2
            case 'ff-only':
582 1
                $options[] = MergeCommand::MERGE_OPTION_FF_ONLY;
583 1
                break;
584 2
            case 'no-ff':
585 1
                $options[] = MergeCommand::MERGE_OPTION_NO_FF;
586 1
                break;
587
        }
588
589 2
        $this->caller->execute(MergeCommand::getInstance($this)->merge($branch, $message, $options));
590
591 2
        return $this;
592
    }
593
594
    /**
595
     * Create a new tag
596
     * This function change the state of the repository on the filesystem
597
     *
598
     * @param string $name       The new tag name
599
     * @param null   $startPoint The reference to create the tag from
600
     * @param null   $message    the tag message
601
     *
602
     * @throws \RuntimeException
603
     * @throws \Symfony\Component\Process\Exception\RuntimeException
604
     * @return Repository
605
     */
606 25
    public function createTag(string $name, $startPoint = null, string $message = null)
607
    {
608 25
        Tag::create($this, $name, $startPoint, $message);
609
610 25
        return $this;
611
    }
612
613
    /**
614
     * Delete a tag by it's name or by passing a Tag object
615
     * This function change the state of the repository on the filesystem
616
     *
617
     * @param string|Tag $tag The tag name or the Tag object
618
     *
619
     * @throws \RuntimeException
620
     * @throws \Symfony\Component\Process\Exception\RuntimeException
621
     * @return Repository
622
     */
623 2
    public function deleteTag($tag)
624
    {
625 2
        if ($tag instanceof Tag) {
626 1
            $tag->delete();
627
        } else {
628 1
            Tag::pick($this, $tag)->delete();
629
        }
630
631 2
        return $this;
632
    }
633
634
    /**
635
     * add a git submodule to the repository
636
     *
637
     * @param string $gitUrl git url of the submodule
638
     * @param string $path   path to register the submodule to
639
     *
640
     * @throws \RuntimeException
641
     * @throws \Symfony\Component\Process\Exception\LogicException
642
     * @throws InvalidArgumentException
643
     * @throws \Symfony\Component\Process\Exception\RuntimeException
644
     * @return Repository
645
     */
646 1
    public function addSubmodule(string $gitUrl, $path = null)
647
    {
648 1
        $this->caller->execute(SubmoduleCommand::getInstance($this)->add($gitUrl, $path));
649
650 1
        return $this;
651
    }
652
653
    /**
654
     * initialize submodules
655
     *
656
     * @param  string $path init only submodules at the specified path
657
     *
658
     * @return Repository
659
     */
660
    public function initSubmodule($path = null)
661
    {
662
        $this->caller->execute(SubmoduleCommand::getInstance($this)->init($path));
663
664
        return $this;
665
    }
666
667
    /**
668
     * update submodules
669
     *
670
     * @param  bool   $recursive update recursively
671
     * @param  bool   $init      init before update
672
     * @param  bool   $force     force the checkout as part of update
673
     * @param  string $path      update only a specific submodule path
674
     *
675
     * @return Repository
676
     */
677
    public function updateSubmodule(
678
        bool $recursive = false,
679
        bool $init = false,
680
        bool $force = false,
681
        $path = null
682
    ) {
683
        $this->caller->execute(SubmoduleCommand::getInstance($this)->update($recursive, $init, $force, $path));
684
685
        return $this;
686
    }
687
688
    /**
689
     * Gets an array of Tag objects
690
     *
691
     * @throws \RuntimeException
692
     * @throws \Symfony\Component\Process\Exception\LogicException
693
     * @throws InvalidArgumentException
694
     * @throws \Symfony\Component\Process\Exception\RuntimeException
695
     * @return array
696
     */
697 4
    public function getTags()
698
    {
699 4
        $tags = [];
700 4
        $this->caller->execute(TagCommand::getInstance($this)->listTags());
701 4
        foreach ($this->caller->getOutputLines() as $tagString) {
702 4
            if ($tagString != '') {
703 4
                $tags[] = new Tag($this, trim($tagString));
704
            }
705
        }
706
707 4
        return $tags;
708
    }
709
710
    /**
711
     * Return a tag object
712
     *
713
     * @param string $name The tag name
714
     *
715
     * @throws \RuntimeException
716
     * @throws \Symfony\Component\Process\Exception\RuntimeException
717
     * @return Tag|null
718
     */
719 28
    public function getTag(string $name)
720
    {
721 28
        $tagFinderOutput = $this->caller
722 28
            ->execute(TagCommand::getInstance()->listTags())
723 28
            ->getOutputLines(true);
724
725 28
        foreach ($tagFinderOutput as $line) {
726 28
            if ($line === $name) {
727 28
                return new Tag($this, $name);
728
            }
729
        }
730
731 1
        return null;
732
    }
733
734
    /**
735
     * Return the last created tag
736
     *
737
     * @throws \LogicException
738
     * @throws \RuntimeException
739
     * @throws \InvalidArgumentException
740
     * @return Tag|null
741
     */
742 1
    public function getLastTag()
743
    {
744 1
        $finder = Finder::create()
745 1
            ->files()
746 1
            ->in(sprintf('%s/.git/refs/tags', $this->path))
747 1
            ->sortByChangedTime();
748
749 1
        if ($finder->count() == 0) {
750
            return null;
751
        }
752
753 1
        $files = iterator_to_array($finder->getIterator(), false);
754 1
        $files = array_reverse($files);
755
        /** @var $firstFile SplFileInfo */
756 1
        $firstFile = $files[0];
757 1
        $tagName = $firstFile->getFilename();
758
759 1
        return Tag::pick($this, $tagName);
760
    }
761
762
    /**
763
     * Try to get a branch or a tag by its name.
764
     *
765
     * @param string $name the reference name (a tag name or a branch name)
766
     *
767
     * @throws \RuntimeException
768
     * @throws \InvalidArgumentException
769
     * @throws \Symfony\Component\Process\Exception\RuntimeException
770
     * @return \GitElephant\Objects\Tag|\GitElephant\Objects\Branch|null
771
     */
772 1
    public function getBranchOrTag(string $name)
773
    {
774 1
        if (in_array($name, $this->getBranches(true))) {
775 1
            return new Branch($this, $name);
776
        }
777 1
        $tagFinderOutput = $this->caller->execute(TagCommand::getInstance($this)->listTags())->getOutputLines(true);
778 1
        foreach ($tagFinderOutput as $line) {
779 1
            if ($line === $name) {
780 1
                return new Tag($this, $name);
781
            }
782
        }
783
784 1
        return null;
785
    }
786
787
    /**
788
     * Return a Commit object
789
     *
790
     * @param string $ref The commit reference
791
     *
792
     * @throws \RuntimeException
793
     * @return Objects\Commit
794
     */
795 15
    public function getCommit($ref = 'HEAD')
796
    {
797 15
        $commit = Commit::pick($this, $ref);
798
799 15
        return $commit;
800
    }
801
802
    /**
803
     * count the commit to arrive to the given treeish
804
     *
805
     * @param string $start
806
     *
807
     * @throws \RuntimeException
808
     * @throws \Symfony\Component\Process\Exception\RuntimeException
809
     * @return int
810
     */
811 3
    public function countCommits($start = 'HEAD')
812
    {
813 3
        $commit = Commit::pick($this, $start);
814
815 3
        return $commit->count();
816
    }
817
818
    /**
819
     * Get a log for a ref
820
     *
821
     * @param string|TreeishInterface|array $ref         the treeish to check, as a string, as an object or as an array
822
     * @param string|NodeObject             $path        the physical path to the tree relative to the repository root
823
     * @param int|null                      $limit       limit to n entries
824
     * @param int|null                      $offset      skip n entries
825
     * @param boolean|false                 $firstParent skip commits brought in to branch by a merge
826
     *
827
     * @return \GitElephant\Objects\Log
828
     */
829 20
    public function getLog(
830
        $ref = 'HEAD',
831
        $path = null,
832
        int $limit = 10,
833
        int $offset = null,
834
        bool $firstParent = false
835
    ) {
836 20
        return new Log($this, $ref, $path, $limit, $offset, $firstParent);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 831 can also be of type object<GitElephant\Objects\NodeObject>; however, GitElephant\Objects\Log::__construct() does only seem to accept string|null, 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...
837
    }
838
839
    /**
840
     * Get a log for a range ref
841
     *
842
     * @param string            $refStart
843
     * @param string            $refEnd
844
     * @param string|NodeObject $path        the physical path to the tree relative to the repository root
845
     * @param int|null          $limit       limit to n entries
846
     * @param int|null          $offset      skip n entries
847
     * @param boolean|false     $firstParent skip commits brought in to branch by a merge
848
     *
849
     * @return \GitElephant\Objects\LogRange|\GitElephant\Objects\Log
850
     */
851
    public function getLogRange(
852
        $refStart,
853
        $refEnd,
854
        $path = null,
855
        int $limit = 10,
856
        int $offset = null,
857
        bool $firstParent = false
858
    ) {
859
        // Handle when clients provide bad start reference on branch creation
860
        if (preg_match('~^[0]+$~', $refStart)) {
861
            return new Log($this, $refEnd, $path, $limit, $offset, $firstParent);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 854 can also be of type object<GitElephant\Objects\NodeObject>; however, GitElephant\Objects\Log::__construct() does only seem to accept string|null, 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...
862
        }
863
864
        // Handle when clients provide bad end reference on branch deletion
865
        if (preg_match('~^[0]+$~', $refEnd)) {
866
            $refEnd = $refStart;
867
        }
868
869
        return new LogRange($this, $refStart, $refEnd, $path, $limit, $offset, $firstParent);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 854 can also be of type object<GitElephant\Objects\NodeObject> or string; however, GitElephant\Objects\LogRange::__construct() does only seem to accept null, 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...
870
    }
871
872
    /**
873
     * Get a log for an object
874
     *
875
     * @param \GitElephant\Objects\NodeObject         $obj    The Object instance
876
     * @param null|string|\GitElephant\Objects\Branch $branch The branch to read from
877
     * @param int                                     $limit  Limit to n entries
878
     * @param int|null                                $offset Skip n entries
879
     *
880
     * @throws \RuntimeException
881
     * @throws \Symfony\Component\Process\Exception\LogicException
882
     * @throws InvalidArgumentException
883
     * @throws \Symfony\Component\Process\Exception\RuntimeException
884
     * @return \GitElephant\Objects\Log
885
     */
886 3
    public function getObjectLog(NodeObject $obj, $branch = null, int $limit = 1, int $offset = null)
887
    {
888 3
        $command = LogCommand::getInstance($this)->showObjectLog($obj, $branch, $limit, $offset);
889
890 3
        return Log::createFromOutputLines($this, $this->caller->execute($command)->getOutputLines());
891
    }
892
893
    /**
894
     * Checkout a branch
895
     * This function change the state of the repository on the filesystem
896
     *
897
     * @param string|TreeishInterface $ref    the reference to checkout
898
     * @param bool                    $create like -b on the command line
899
     *
900
     * @throws \RuntimeException
901
     * @throws \Symfony\Component\Process\Exception\LogicException
902
     * @throws InvalidArgumentException
903
     * @throws \Symfony\Component\Process\Exception\RuntimeException
904
     * @return Repository
905
     */
906 24
    public function checkout($ref, bool $create = false)
907
    {
908 24
        if ($create && is_null($this->getBranch($ref))) {
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 906 can also be of type object<GitElephant\Objects\TreeishInterface>; however, GitElephant\Repository::getBranch() 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...
909
            $this->createBranch($ref);
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 906 can also be of type object<GitElephant\Objects\TreeishInterface>; however, GitElephant\Repository::createBranch() 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...
910
        }
911 24
        $this->caller->execute(MainCommand::getInstance($this)->checkout($ref));
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 906 can also be of type object<GitElephant\Objects\TreeishInterface>; however, GitElephant\Command\MainCommand::checkout() does only seem to accept string|object<GitElephant\Objects\Branch>, 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...
912
913 24
        return $this;
914
    }
915
916
    /**
917
     * Retrieve an instance of Tree
918
     * Tree Object is Countable, Iterable and has ArrayAccess for easy manipulation
919
     *
920
     * @param string|TreeishInterface $ref  the treeish to check
921
     * @param string|NodeObject       $path Object or null for root
922
     *
923
     * @throws \RuntimeException
924
     * @throws \Symfony\Component\Process\Exception\LogicException
925
     * @throws InvalidArgumentException
926
     * @throws \Symfony\Component\Process\Exception\RuntimeException
927
     * @return Objects\Tree
928
     */
929 15
    public function getTree($ref = 'HEAD', $path = null)
930
    {
931 15
        if (is_string($path) && '' !== $path) {
932
            $outputLines = $this
933 9
                ->getCaller()
934 9
                ->execute(LsTreeCommand::getInstance($this)->tree($ref, $path))
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 929 can also be of type object<GitElephant\Objects\TreeishInterface>; however, GitElephant\Command\LsTreeCommand::tree() 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...
935 9
                ->getOutputLines(true);
936
937 9
            $path = TreeObject::createFromOutputLine($this, $outputLines[0]);
938
        }
939
940 15
        return new Tree($this, $ref, $path);
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 929 can also be of type object<GitElephant\Objects\TreeishInterface>; however, GitElephant\Objects\Tree::__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...
Bug introduced by
It seems like $path defined by parameter $path on line 929 can also be of type string; however, GitElephant\Objects\Tree::__construct() does only seem to accept null|object<GitElephant\Objects\NodeObject>, 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...
941
    }
942
943
    /**
944
     * Get a Diff object for a commit with its parent, by default the diff is between the current head and its parent
945
     *
946
     * @param \GitElephant\Objects\Commit|string      $commit1 A TreeishInterface instance
947
     * @param \GitElephant\Objects\Commit|string|null $commit2 A TreeishInterface instance
948
     * @param null|string|NodeObject                  $path    The path to get the diff for or a Object instance
949
     *
950
     * @throws \RuntimeException
951
     * @throws \InvalidArgumentException
952
     * @return Objects\Diff\Diff
953
     */
954 2
    public function getDiff(string $commit1 = null, string $commit2 = null, string $path = null)
955
    {
956 2
        return Diff::create($this, $commit1, $commit2, $path);
957
    }
958
959
    /**
960
     * Clone a repository
961
     *
962
     * @param string      $url           the repository url (i.e. git://github.com/matteosister/GitElephant.git)
963
     * @param null        $to            where to clone the repo
964
     * @param string|null $repoReference Repo reference to clone. Required if performing a shallow clone.
965
     * @param int|null    $depth         Depth to clone repo. Specify 1 to perform a shallow clone
966
     * @param bool        $recursive     Whether to recursively clone child repos.
967
     *
968
     * @throws \RuntimeException
969
     * @throws \Symfony\Component\Process\Exception\LogicException
970
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
971
     * @throws \Symfony\Component\Process\Exception\RuntimeException
972
     * @return Repository
973
     */
974 2
    public function cloneFrom(string $url, string $to = null, string $repoReference = null, int $depth = null, bool $recursive = false)
975
    {
976 2
        $command = (Command\CloneCommand::getInstance($this))->cloneUrl($url, $to, $repoReference, $depth, $recursive);
977 2
        $this->caller->execute($command);
978 2
        return $this;
979
    }
980
981
    /**
982
     * @param string $name remote name
983
     * @param string $url  remote url
984
     *
985
     * @throws \RuntimeException
986
     * @throws \Symfony\Component\Process\Exception\LogicException
987
     * @throws InvalidArgumentException
988
     * @throws \Symfony\Component\Process\Exception\RuntimeException
989
     * @return Repository
990
     */
991 7
    public function addRemote(string $name, string $url)
992
    {
993 7
        $this->caller->execute(RemoteCommand::getInstance($this)->add($name, $url));
994
995 7
        return $this;
996
    }
997
998
    /**
999
     * @param string $name         remote name
1000
     * @param bool   $queryRemotes Fetch new information from remotes
1001
     *
1002
     * @return \GitElephant\Objects\Remote
1003
     */
1004 1
    public function getRemote(string $name, bool $queryRemotes = true)
1005
    {
1006 1
        return Remote::pick($this, $name, $queryRemotes);
1007
    }
1008
1009
    /**
1010
     * gets a list of remote objects
1011
     *
1012
     * @param bool $queryRemotes Fetch new information from remotes
1013
     *
1014
     * @throws \RuntimeException
1015
     * @throws \Symfony\Component\Process\Exception\LogicException
1016
     * @throws InvalidArgumentException
1017
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1018
     * @return array
1019
     */
1020 1
    public function getRemotes(bool $queryRemotes = true)
1021
    {
1022 1
        $remoteNames = $this->caller
1023 1
            ->execute(RemoteCommand::getInstance($this)->show(null, $queryRemotes))
1024 1
            ->getOutputLines(true);
1025
1026 1
        $remotes = [];
1027 1
        foreach ($remoteNames as $remoteName) {
1028 1
            $remotes[] = $this->getRemote($remoteName, $queryRemotes);
1029
        }
1030
1031 1
        return $remotes;
1032
    }
1033
1034
    /**
1035
     * Download objects and refs from another repository
1036
     *
1037
     * @param string $from
1038
     * @param string $ref
1039
     * @param bool   $tags
1040
     *
1041
     * @throws \RuntimeException
1042
     * @throws \Symfony\Component\Process\Exception\LogicException
1043
     * @throws InvalidArgumentException
1044
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1045
     */
1046 1
    public function fetch($from = null, $ref = null, bool $tags = false)
1047
    {
1048 1
        $options = [];
1049 1
        if ($tags === true) {
1050 1
            $options = ['--tags'];
1051
        }
1052 1
        $this->caller->execute(FetchCommand::getInstance($this)->fetch($from, $ref, $options));
1053 1
    }
1054
1055
    /**
1056
     * Fetch from and merge with another repository or a local branch
1057
     *
1058
     * @param string $from
1059
     * @param string $ref
1060
     * @param bool   $rebase
1061
     *
1062
     * @throws \RuntimeException
1063
     * @throws \Symfony\Component\Process\Exception\LogicException
1064
     * @throws InvalidArgumentException
1065
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1066
     */
1067 2
    public function pull($from = null, $ref = null, bool $rebase = true)
1068
    {
1069 2
        $this->caller->execute(PullCommand::getInstance($this)->pull($from, $ref, $rebase));
1070 2
    }
1071
1072
    /**
1073
     * Push changes to remote repository
1074
     *
1075
     * @param string $to
1076
     * @param string $ref
1077
     * @param string $args
1078
     *
1079
     * @throws \RuntimeException
1080
     * @throws \Symfony\Component\Process\Exception\LogicException
1081
     * @throws InvalidArgumentException
1082
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1083
     */
1084 1
    public function push($to = null, $ref = null, string $args = null)
1085
    {
1086 1
        $this->caller->execute(PushCommand::getInstance($this)->push($to, $ref, $args));
1087 1
    }
1088
1089
    /**
1090
     * get the humanish name of the repository
1091
     *
1092
     * @return string
1093
     */
1094 2
    public function getHumanishName()
1095
    {
1096 2
        $name = substr($this->getPath(), strrpos($this->getPath(), '/') + 1);
1097 2
        $name = str_replace('.git', '.', $name);
1098 2
        $name = str_replace('.bundle', '.', $name);
1099
1100 2
        return $name;
1101
    }
1102
1103
    /**
1104
     * output a node content as an array of lines
1105
     *
1106
     * @param \GitElephant\Objects\NodeObject              $obj     The Object of type BLOB
1107
     * @param \GitElephant\Objects\TreeishInterface|string $treeish A treeish object
1108
     *
1109
     * @throws \RuntimeException
1110
     * @throws \Symfony\Component\Process\Exception\LogicException
1111
     * @throws InvalidArgumentException
1112
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1113
     * @return array
1114
     */
1115 1
    public function outputContent(NodeObject $obj, $treeish)
1116
    {
1117 1
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1118
1119 1
        return $this->caller->execute($command)->getOutputLines();
1120
    }
1121
1122
    /**
1123
     * output a node raw content
1124
     *
1125
     * @param \GitElephant\Objects\NodeObject              $obj     The Object of type BLOB
1126
     * @param \GitElephant\Objects\TreeishInterface|string $treeish A treeish object
1127
     *
1128
     * @throws \RuntimeException
1129
     * @throws \Symfony\Component\Process\Exception\LogicException
1130
     * @throws InvalidArgumentException
1131
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1132
     * @return string
1133
     */
1134
    public function outputRawContent(NodeObject $obj, $treeish)
1135
    {
1136
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1137
1138
        return $this->caller->execute($command)->getRawOutput();
1139
    }
1140
1141
    /**
1142
     * Get the path
1143
     *
1144
     * @return string
1145
     */
1146 64
    public function getPath()
1147
    {
1148 64
        return $this->path;
1149
    }
1150
1151
    /**
1152
     * Get the repository name
1153
     *
1154
     * @return string
1155
     */
1156 1
    public function getName()
1157
    {
1158 1
        return $this->name;
1159
    }
1160
1161
    /**
1162
     * Set the repository name
1163
     *
1164
     * @param string $name the repository name
1165
     */
1166 1
    public function setName(string $name)
1167
    {
1168 1
        $this->name = $name;
1169 1
    }
1170
1171
    /**
1172
     * Caller setter
1173
     *
1174
     * @param \GitElephant\Command\Caller\Caller $caller the caller variable
1175
     */
1176
    public function setCaller(Caller $caller)
1177
    {
1178
        $this->caller = $caller;
1179
    }
1180
1181
    /**
1182
     * Caller getter
1183
     *
1184
     * @return \GitElephant\Command\Caller\Caller
1185
     */
1186 91
    public function getCaller()
1187
    {
1188 91
        return $this->caller;
1189
    }
1190
1191
    /**
1192
     * get global config list
1193
     *
1194
     * @return array Global config list
1195
     */
1196 97
    public function getGlobalConfigs()
1197
    {
1198 97
        return $this->globalConfigs;
1199
    }
1200
1201
    /**
1202
     * add a key/value pair to the global config list
1203
     *
1204
     * @param string $name  The config name
1205
     * @param mixed  $value The config value
1206
     */
1207 1
    public function addGlobalConfig(string $name, $value)
1208
    {
1209 1
        $this->globalConfigs[$name] = $value;
1210 1
    }
1211
1212
    /**
1213
     * remove an element form the global config list, identified by key
1214
     *
1215
     * @param  string $name The config name
1216
     */
1217 1
    public function removeGlobalConfig(string $name)
1218
    {
1219 1
        if (isset($this->globalConfigs[$name])) {
1220 1
            unset($this->globalConfigs[$name]);
1221
        }
1222 1
    }
1223
1224
    /**
1225
     * get global options list
1226
     *
1227
     * @return array Global options list
1228
     */
1229 97
    public function getGlobalOptions()
1230
    {
1231 97
        return $this->globalOptions;
1232
    }
1233
1234
    /**
1235
     * add a key/value pair to the global option list
1236
     *
1237
     * @param string $name  The option name
1238
     * @param mixed  $value The option value
1239
     */
1240 1
    public function addGlobalOption(string $name, $value)
1241
    {
1242 1
        $this->globalOptions[$name] = $value;
1243 1
    }
1244
1245
    /**
1246
     * remove an element form the global option list, identified by key
1247
     *
1248
     * @param  string $name The option name
1249
     */
1250 1
    public function removeGlobalOption(string $name)
1251
    {
1252 1
        if (isset($this->globalOptions[$name])) {
1253 1
            unset($this->globalOptions[$name]);
1254
        }
1255 1
    }
1256
1257
    /**
1258
     * get global command arguments list
1259
     *
1260
     * @return array Global command arguments list
1261
     */
1262 97
    public function getGlobalCommandArguments()
1263
    {
1264 97
        return $this->globalCommandArguments;
1265
    }
1266
1267
    /**
1268
     * add a value to the global command argument list
1269
     *
1270
     * @param string $value The command argument
1271
     */
1272 1
    public function addGlobalCommandArgument($value)
1273
    {
1274 1
        if (!in_array($value, $this->globalCommandArguments, true)) {
1275 1
            $this->globalCommandArguments[] = $value;
1276
        }
1277 1
    }
1278
1279
    /**
1280
     * remove an element form the global command argument list, identified by
1281
     * value
1282
     *
1283
     * @param  string $value The command argument
1284
     */
1285 1
    public function removeGlobalCommandArgument($value)
1286
    {
1287 1
        if (in_array($value, $this->globalCommandArguments, true)) {
1288 1
            $index = array_search($value, $this->globalCommandArguments);
1289 1
            unset($this->globalCommandArguments[$index]);
1290
        }
1291 1
    }
1292
1293
    /**
1294
     *  Save your local modifications to a new stash, and run git reset --hard to revert them.
1295
     *
1296
     * @param string|null $message
1297
     * @param boolean     $includeUntracked
1298
     * @param boolean     $keepIndex
1299
     */
1300 2
    public function stash(string $message = null, bool $includeUntracked = false, bool $keepIndex = false)
1301
    {
1302 2
        $stashCommand = StashCommand::getInstance($this);
1303 2
        $command = $stashCommand->save($message, $includeUntracked, $keepIndex);
1304 2
        $this->caller->execute($command);
1305 1
    }
1306
1307
    /**
1308
     * Shows stash list
1309
     *
1310
     * @param array|null $options
1311
     *
1312
     * @return array
1313
     */
1314 1
    public function stashList(array $options = null)
1315
    {
1316 1
        $stashCommand = StashCommand::getInstance($this);
1317 1
        $command = $stashCommand->listStashes($options);
1318 1
        $this->caller->execute($command);
1319
1320 1
        return array_map('trim', $this->caller->getOutputLines(true));
1321
    }
1322
1323
    /**
1324
     * Shows details for a stash
1325
     *
1326
     * @param string $stash
1327
     *
1328
     * @return string
1329
     */
1330 1
    public function stashShow(string $stash)
1331
    {
1332 1
        $stashCommand = StashCommand::getInstance($this);
1333 1
        $command = $stashCommand->show($stash);
1334 1
        $this->caller->execute($command);
1335
1336 1
        return $this->caller->getOutput();
1337
    }
1338
1339
    /**
1340
     * Drops a stash
1341
     *
1342
     * @param string $stash
1343
     */
1344 1
    public function stashDrop(string $stash)
1345
    {
1346 1
        $stashCommand = StashCommand::getInstance($this);
1347 1
        $command = $stashCommand->drop($stash);
1348 1
        $this->caller->execute($command);
1349 1
    }
1350
1351
    /**
1352
     * Applies a stash
1353
     *
1354
     * @param string  $stash
1355
     * @param boolean $index
1356
     */
1357 1
    public function stashApply(string $stash, bool $index = false)
1358
    {
1359 1
        $stashCommand = StashCommand::getInstance($this);
1360 1
        $command = $stashCommand->apply($stash, $index);
1361 1
        $this->caller->execute($command);
1362 1
    }
1363
1364
    /**
1365
     *  Applies a stash, then removes it from the stash
1366
     *
1367
     * @param string  $stash
1368
     * @param boolean $index
1369
     */
1370 1
    public function stashPop(string $stash, bool $index = false)
1371
    {
1372 1
        $stashCommand = StashCommand::getInstance($this);
1373 1
        $command = $stashCommand->pop($stash, $index);
1374 1
        $this->caller->execute($command);
1375 1
    }
1376
1377
    /**
1378
     *  Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created
1379
     *
1380
     * @param string $branch
1381
     * @param string $stash
1382
     */
1383 1
    public function stashBranch(string $branch, string $stash)
1384
    {
1385 1
        $stashCommand = StashCommand::getInstance($this);
1386 1
        $command = $stashCommand->branch($branch, $stash);
1387 1
        $this->caller->execute($command);
1388 1
    }
1389
1390
    /**
1391
     *  Save your local modifications to a new stash, and run git reset --hard to revert them.
1392
     *
1393
     */
1394
    public function stashClear()
1395
    {
1396
        $stashCommand = StashCommand::getInstance($this);
1397
        $command = $stashCommand->clear();
1398
        $this->caller->execute($command);
1399
    }
1400
1401
    /**
1402
     *  Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the
1403
     *  ref namespace.
1404
     *
1405
     * @return string
1406
     */
1407 1
    public function stashCreate()
1408
    {
1409 1
        $stashCommand = StashCommand::getInstance($this);
1410 1
        $command = $stashCommand->clear();
1411 1
        $this->caller->execute($command);
1412
1413 1
        return $this->caller->getOutput();
1414
    }
1415
}
1416