Completed
Pull Request — develop (#148)
by
unknown
05:08 queued 03:19
created

Repository::removeGlobalOption()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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