Completed
Push — develop ( 700ecb...e69eaa )
by Matteo
02:36
created

Repository::stashClear()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
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\Object;
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
58
/**
59
 * Repository
60
 *
61
 * Base Class for repository operations
62
 *
63
 * @author Matteo Giachino <[email protected]>
64
 * @author Dhaval Patel <[email protected]>
65
 * @author Kirk Madera <[email protected]>
66
 */
67
class Repository
68
{
69
    /**
70
     * the repository path
71
     *
72
     * @var string
73
     */
74
    private $path;
75
76
    /**
77
     * the caller instance
78
     *
79
     * @var \GitElephant\Command\Caller\Caller
80
     */
81
    private $caller;
82
83
    /**
84
     * A general repository name
85
     *
86
     * @var string $name the repository name
87
     */
88
    private $name;
89
90
    /**
91
     * A list of global configs to apply to every command
92
     *
93
     * @var array
94
     */
95
    private $globalConfigs = array();
96
97
    /**
98
     * A list of global options to apply to every command
99
     *
100
     * @var array
101
     */
102
    private $globalOptions = array();
103
104
    /**
105
     * A list of global arguments to apply to every command
106
     *
107
     * @var array
108
     */
109
    private $globalCommandArguments = array();
110
111
    /**
112
     * Class constructor
113
     *
114
     * @param string         $repositoryPath the path of the git repository
115
     * @param GitBinary|null $binary         the GitBinary instance that calls the commands
116
     * @param string         $name           a repository name
117
     *
118
     * @throws Exception\InvalidRepositoryPathException
119
     */
120 105
    public function __construct($repositoryPath, GitBinary $binary = null, $name = null)
121
    {
122 105
        if (is_null($binary)) {
123 105
            $binary = new GitBinary();
124
        }
125
126 105
        $this->path = $repositoryPath;
127 105
        $this->caller = new Caller($binary, $repositoryPath);
128 105
        $this->name = $name;
129 105
    }
130
131
    /**
132
     * Factory method
133
     *
134
     * @param string         $repositoryPath the path of the git repository
135
     * @param GitBinary|null $binary         the GitBinary instance that calls the commands
136
     * @param string         $name           a repository name
137
     *
138
     * @return \GitElephant\Repository
139
     */
140 104
    public static function open($repositoryPath, GitBinary $binary = null, $name = null)
141
    {
142 104
        return new self($repositoryPath, $binary, $name);
143
    }
144
145
    /**
146
     * create a repository from a remote git url, or a local filesystem
147
     * and save it in a temp folder
148
     *
149
     * @param string|Repository $git            the git remote url, or the filesystem path
150
     * @param null              $repositoryPath path
151
     * @param GitBinary         $binary         binary
152
     * @param null              $name           repository name
153
     *
154
     * @throws \RuntimeException
155
     * @throws \Symfony\Component\Filesystem\Exception\IOException
156
     * @return Repository
157
     */
158 1
    public static function createFromRemote($git, $repositoryPath = null, GitBinary $binary = null, $name = null)
159
    {
160 1
        if (null === $repositoryPath) {
161 1
            $tempDir = realpath(sys_get_temp_dir());
162 1
            $repositoryPath = sprintf('%s%s%s', $tempDir, DIRECTORY_SEPARATOR, sha1(uniqid()));
163 1
            $fs = new Filesystem();
164 1
            $fs->mkdir($repositoryPath);
165
        }
166 1
        $repository = new Repository($repositoryPath, $binary, $name);
167 1
        if ($git instanceof Repository) {
168
            $git = $git->getPath();
169
        }
170 1
        $repository->cloneFrom($git, $repositoryPath);
0 ignored issues
show
Bug introduced by
It seems like $repositoryPath defined by sprintf('%s%s%s', $tempD...ARATOR, sha1(uniqid())) on line 162 can also be of type string; however, GitElephant\Repository::cloneFrom() does only seem to accept null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
171 1
        $repository->checkoutAllRemoteBranches();
172
173 1
        return $repository;
174
    }
175
176
    /**
177
     * Init the repository
178
     *
179
     * @param bool $bare created a bare repository
180
     *
181
     * @throws \RuntimeException
182
     * @throws \Symfony\Component\Process\Exception\LogicException
183
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
184
     * @throws \Symfony\Component\Process\Exception\RuntimeException
185
     * @return Repository
186
     */
187 94
    public function init($bare = false)
188
    {
189 94
        $this->caller->execute(MainCommand::getInstance($this)->init($bare));
190
191 94
        return $this;
192
    }
193
194
    /**
195
     * Stage the working tree content
196
     *
197
     * @param string|Object $path the path to store
198
     *
199
     * @throws \RuntimeException
200
     * @throws \Symfony\Component\Process\Exception\LogicException
201
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
202
     * @throws \Symfony\Component\Process\Exception\RuntimeException
203
     * @return Repository
204
     */
205 90
    public function stage($path = '.')
206
    {
207 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 205 can also be of type object<GitElephant\Objects\Object>; 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...
208
209 90
        return $this;
210
    }
211
212
    /**
213
     * Unstage a tree content
214
     *
215
     * @param string|Object $path the path to unstage
216
     *
217
     * @throws \RuntimeException
218
     * @throws \Symfony\Component\Process\Exception\LogicException
219
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
220
     * @throws \Symfony\Component\Process\Exception\RuntimeException
221
     * @return Repository
222
     */
223 2
    public function unstage($path)
224
    {
225 2
        $this->caller->execute(MainCommand::getInstance($this)->unstage($path), true, null, array(0, 1));
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 223 can also be of type object<GitElephant\Objects\Object>; 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...
226
227 2
        return $this;
228
    }
229
230
    /**
231
     * Move a file/directory
232
     *
233
     * @param string|Object $from source path
234
     * @param string|Object $to   destination path
235
     *
236
     * @throws \RuntimeException
237
     * @throws \Symfony\Component\Process\Exception\LogicException
238
     * @throws \InvalidArgumentException
239
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
240
     * @throws \Symfony\Component\Process\Exception\RuntimeException
241
     * @return Repository
242
     */
243 1
    public function move($from, $to)
244
    {
245 1
        $this->caller->execute(MainCommand::getInstance($this)->move($from, $to));
246
247 1
        return $this;
248
    }
249
250
    /**
251
     * Remove a file/directory
252
     *
253
     * @param string|Object $path      the path to remove
254
     * @param bool          $recursive recurse
255
     * @param bool          $force     force
256
     *
257
     * @throws \RuntimeException
258
     * @throws \Symfony\Component\Process\Exception\LogicException
259
     * @throws \InvalidArgumentException
260
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
261
     * @throws \Symfony\Component\Process\Exception\RuntimeException
262
     * @return Repository
263
     */
264 1
    public function remove($path, $recursive = false, $force = false)
265
    {
266 1
        $this->caller->execute(MainCommand::getInstance($this)->remove($path, $recursive, $force));
267
268 1
        return $this;
269
    }
270
271
    /**
272
     * Commit content to the repository, eventually staging all unstaged content
273
     *
274
     * @param string        $message  the commit message
275
     * @param bool          $stageAll whether to stage on not everything before commit
276
     * @param string|null   $ref      the reference to commit to (checkout -> commit -> checkout previous)
277
     * @param string|Author $author   override the author for this commit
278
     *
279
     * @throws \RuntimeException
280
     * @throws \InvalidArgumentException
281
     * @throws \Symfony\Component\Process\Exception\RuntimeException
282
     * @return Repository
283
     */
284 85
    public function commit($message, $stageAll = false, $ref = null, $author = null, $allowEmpty = false)
285
    {
286 85
        $currentBranch = null;
287 85
        if (! is_null($ref)) {
288 1
            $currentBranch = $this->getMainBranch();
289 1
            $this->checkout($ref);
290
        }
291 85
        if ($stageAll) {
292 83
            $this->stage();
293
        }
294 85
        $this->caller->execute(MainCommand::getInstance($this)->commit($message, $stageAll, $author, $allowEmpty));
295 85
        if (! is_null($ref)) {
296 1
            $this->checkout($currentBranch);
0 ignored issues
show
Bug introduced by
It seems like $currentBranch defined by null on line 286 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...
297
        }
298
299 85
        return $this;
300
    }
301
302
    /**
303
     * rev-parse command - often used to return a commit tag.
304
     *
305
     * @param array                  $options the options to apply to rev-parse
306
     * @param string|Object|Commit   $arg the argument (may be a branch head, etc)
307
     *
308
     * @throws \RuntimeException
309
     * @throws \InvalidArgumentException
310
     * @throws \Symfony\Component\Process\Exception\RuntimeException
311
     * @return array
312
     */
313 1
    public function revParse($arg = null, array $options = array())
314
    {
315 1
        $this->caller->execute(RevParseCommand::getInstance()->revParse($arg, $options));
0 ignored issues
show
Bug introduced by
It seems like $arg defined by parameter $arg on line 313 can also be of type object<GitElephant\Objects\Commit> or object<GitElephant\Objects\Object>; however, GitElephant\Command\RevParseCommand::revParse() does only seem to accept object<GitElephant\Objects\Branch>|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...
316
317 1
        return array_map('trim', $this->caller->getOutputLines(true));
318
    }
319
320
    /**
321
     * Check if this is a bare repository
322
     * @return boolean
323
     */
324 1
    public function isBare()
325
    {
326 1
        $options = array(RevParseCommand::OPTION_IS_BARE_REPOSIORY);
327 1
        $this->caller->execute(RevParseCommand::getInstance()->revParse(null, $options));
328
329 1
        return trim($this->caller->getOutput()) === 'true';
330
    }
331
332
    /**
333
     * @param TreeishInterface|Commit|string $arg
334
     * @param array $options
335
     */
336 2
    public function reset($arg, $options)
337
    {
338 2
        $this->caller->execute(ResetCommand::getInstance($this)->reset($arg,$options));
339 2
    }
340
341
    /**
342
     * Get the repository status
343
     *
344
     * @return Status
345
     */
346 5
    public function getStatus()
347
    {
348 5
        return Status::get($this);
349
    }
350
351
    /**
352
     * @return StatusWorkingTree
353
     */
354 1
    public function getWorkingTreeStatus()
355
    {
356 1
        return StatusWorkingTree::get($this);
357
    }
358
359
    /**
360
     * @return StatusIndex
361
     */
362 4
    public function getIndexStatus()
363
    {
364 4
        return StatusIndex::get($this);
365
    }
366
367
    /**
368
     * isClean Return true if the repository is not dirty.
369
     *
370
     * @return boolean
371
     */
372
    public function isClean()
373
    {
374
        return $this->getStatus()->all()->isEmpty();
375
    }
376
377
    /**
378
     * isDirty Return true if the repository has some modified files.
379
     *
380
     * @return boolean
381
     */
382
    public function isDirty()
383
    {
384
        return !$this->isClean();
385
    }
386
387
    /**
388
     * Get the repository status as a string
389
     *
390
     * @throws \RuntimeException
391
     * @throws \Symfony\Component\Process\Exception\LogicException
392
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
393
     * @throws \Symfony\Component\Process\Exception\RuntimeException
394
     * @return array
395
     */
396 4
    public function getStatusOutput()
397
    {
398 4
        $this->caller->execute(MainCommand::getInstance($this)->status());
399
400 4
        return array_map('trim', $this->caller->getOutputLines());
401
    }
402
403
    /**
404
     * Create a new branch
405
     *
406
     * @param string $name       the new branch name
407
     * @param null   $startPoint the reference to create the branch from
408
     *
409
     * @throws \RuntimeException
410
     * @throws \Symfony\Component\Process\Exception\RuntimeException
411
     * @return Repository
412
     */
413 27
    public function createBranch($name, $startPoint = null)
414
    {
415 27
        Branch::create($this, $name, $startPoint);
416
417 27
        return $this;
418
    }
419
420
    /**
421
     * Delete a branch by its name
422
     * This function change the state of the repository on the filesystem
423
     *
424
     * @param string $name  The branch to delete
425
     * @param bool   $force Force the delete
426
     *
427
     * @throws \RuntimeException
428
     * @throws \Symfony\Component\Process\Exception\LogicException
429
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
430
     * @throws \Symfony\Component\Process\Exception\RuntimeException
431
     * @return Repository
432
     */
433 1
    public function deleteBranch($name, $force = false)
434
    {
435 1
        $this->caller->execute(BranchCommand::getInstance($this)->delete($name, $force));
436
437 1
        return $this;
438
    }
439
440
    /**
441
     * An array of Branch objects
442
     *
443
     * @param bool $namesOnly return an array of branch names as a string
444
     * @param bool $all       lists also remote branches
445
     *
446
     * @throws \RuntimeException
447
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
448
     * @throws \Symfony\Component\Process\Exception\LogicException
449
     * @throws \InvalidArgumentException
450
     * @throws \Symfony\Component\Process\Exception\RuntimeException
451
     * @return array
452
     */
453 17
    public function getBranches($namesOnly = false, $all = false)
454
    {
455 17
        $branches = array();
456 17
        if ($namesOnly) {
457 6
            $outputLines = $this->caller->execute(
458 6
                BranchCommand::getInstance($this)->listBranches($all, true)
459 6
            )->getOutputLines(true);
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->execute(
468 14
                BranchCommand::getInstance($this)->listBranches($all)
469 14
            )->getOutputLines(true);
470 14
            foreach ($outputLines as $branchLine) {
471 14
                $branches[] = Branch::createFromOutputLine($this, $branchLine);
472
            }
473
        }
474
475 17
        return $branches;
476
    }
477
478
    /**
479
     * Return the actually checked out branch
480
     *
481
     * @throws \RuntimeException
482
     * @throws \InvalidArgumentException
483
     * @throws \Symfony\Component\Process\Exception\RuntimeException
484
     * @return Objects\Branch
485
     */
486 5
    public function getMainBranch()
487
    {
488 5
        $filtered = array_filter(
489 5
            $this->getBranches(),
490
            function (Branch $branch) {
491 5
                return $branch->getCurrent();
492 5
            }
493
        );
494 5
        sort($filtered);
495
496 5
        return $filtered[0];
497
    }
498
499
    /**
500
     * Retrieve a Branch object by a branch name
501
     *
502
     * @param string $name The branch name
503
     *
504
     * @throws \RuntimeException
505
     * @throws \InvalidArgumentException
506
     * @throws \Symfony\Component\Process\Exception\RuntimeException
507
     * @return null|Branch
508
     */
509 9
    public function getBranch($name)
510
    {
511
        /** @var Branch $branch */
512 9
        foreach ($this->getBranches() as $branch) {
513 9
            if ($branch->getName() == $name) {
514 9
                return $branch;
515
            }
516
        }
517
518 1
        return null;
519
    }
520
521
    /**
522
     * Checkout all branches from the remote and make them local
523
     *
524
     * @param string $remote remote to fetch from
525
     *
526
     * @throws \RuntimeException
527
     * @throws \InvalidArgumentException
528
     * @throws \Symfony\Component\Process\Exception\RuntimeException
529
     * @return Repository
530
     */
531 1
    public function checkoutAllRemoteBranches($remote = 'origin')
532
    {
533 1
        $actualBranch = $this->getMainBranch();
534 1
        $actualBranches = $this->getBranches(true, false);
535 1
        $allBranches = $this->getBranches(true, true);
536 1
        $realBranches = array_filter(
537 1
            $allBranches,
538 1
            function ($branch) use ($actualBranches) {
539 1
                return !in_array($branch, $actualBranches)
540 1
                && preg_match('/^remotes(.+)$/', $branch)
541 1
                && !preg_match('/^(.+)(HEAD)(.*?)$/', $branch);
542 1
            }
543
        );
544 1
        foreach ($realBranches as $realBranch) {
545 1
            $this->checkout(str_replace(sprintf('remotes/%s/', $remote), '', $realBranch));
546
        }
547 1
        $this->checkout($actualBranch);
548
549 1
        return $this;
550
    }
551
552
    /**
553
     * Merge a Branch in the current checked out branch
554
     *
555
     * @param Objects\Branch $branch  The branch to merge in the current checked out branch
556
     * @param string         $message The message for the merge commit, if merge is 3-way
557
     * @param string         $mode    The merge mode: ff-only, no-ff or auto
558
     *
559
     * @throws \RuntimeException
560
     * @throws \Symfony\Component\Process\Exception\LogicException
561
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
562
     * @throws \Symfony\Component\Process\Exception\RuntimeException
563
     * @return Repository
564
     */
565 2
    public function merge(Branch $branch, $message = '', $mode = 'auto')
566
    {
567
        $valid_modes = array(
568 2
            'auto',    // deafult git behavior
569
            'ff-only', // force fast forward merge
570
            'no-ff',   // force 3-way merge
571
        );
572 2
        if (!in_array($mode, $valid_modes)) {
573
            throw new \Symfony\Component\Process\Exception\InvalidArgumentException("Invalid merge mode: $mode.");
574
        }
575
576 2
        $options = array();
577
        switch ($mode) {
578 2
            case 'ff-only':
579 1
                $options[] = MergeCommand::MERGE_OPTION_FF_ONLY;
580 1
                break;
581 2
            case 'no-ff':
582 1
                $options[] = MergeCommand::MERGE_OPTION_NO_FF;
583 1
                break;
584
        }
585
586 2
        $this->caller->execute(MergeCommand::getInstance($this)->merge($branch, $message, $options));
587
588 2
        return $this;
589
    }
590
591
    /**
592
     * Create a new tag
593
     * This function change the state of the repository on the filesystem
594
     *
595
     * @param string $name       The new tag name
596
     * @param null   $startPoint The reference to create the tag from
597
     * @param null   $message    the tag message
598
     *
599
     * @throws \RuntimeException
600
     * @throws \Symfony\Component\Process\Exception\RuntimeException
601
     * @return Repository
602
     */
603 25
    public function createTag($name, $startPoint = null, $message = null)
604
    {
605 25
        Tag::create($this, $name, $startPoint, $message);
606
607 25
        return $this;
608
    }
609
610
    /**
611
     * Delete a tag by it's name or by passing a Tag object
612
     * This function change the state of the repository on the filesystem
613
     *
614
     * @param string|Tag $tag The tag name or the Tag object
615
     *
616
     * @throws \RuntimeException
617
     * @throws \Symfony\Component\Process\Exception\RuntimeException
618
     * @return Repository
619
     */
620 2
    public function deleteTag($tag)
621
    {
622 2
        if ($tag instanceof Tag) {
623 1
            $tag->delete();
624
        } else {
625 1
            Tag::pick($this, $tag)->delete();
626
        }
627
628 2
        return $this;
629
    }
630
631
    /**
632
     * add a git submodule to the repository
633
     *
634
     * @param string $gitUrl git url of the submodule
635
     * @param string $path   path to register the submodule to
636
     *
637
     * @throws \RuntimeException
638
     * @throws \Symfony\Component\Process\Exception\LogicException
639
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
640
     * @throws \Symfony\Component\Process\Exception\RuntimeException
641
     * @return Repository
642
     */
643 1
    public function addSubmodule($gitUrl, $path = null)
644
    {
645 1
        $this->caller->execute(SubmoduleCommand::getInstance($this)->add($gitUrl, $path));
646
647 1
        return $this;
648
    }
649
650
    /**
651
     * initialize submodules
652
     *
653
     * @param  string $path init only submodules at the specified path
654
     *
655
     * @return Repository
656
     */
657
    public function initSubmodule($path = null)
658
    {
659
        $this->caller->execute(SubmoduleCommand::getInstance($this)->init($path));
660
        return $this;
661
    }
662
663
    /**
664
     * update submodules
665
     *
666
     * @param  bool   $recursive update recursively
667
     * @param  bool   $init      init before update
668
     * @param  bool   $force     force the checkout as part of update
669
     * @param  string $path      update only a specific submodule path
670
     *
671
     * @return Repository
672
     */
673
    public function updateSubmodule($recursive = false, $init = false, $force = false, $path = null)
674
    {
675
        $this->caller->execute(SubmoduleCommand::getInstance($this)->update($recursive, $init, $force, $path));
676
        return $this;
677
    }
678
679
    /**
680
     * Gets an array of Tag objects
681
     *
682
     * @throws \RuntimeException
683
     * @throws \Symfony\Component\Process\Exception\LogicException
684
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
685
     * @throws \Symfony\Component\Process\Exception\RuntimeException
686
     * @return array
687
     */
688 4
    public function getTags()
689
    {
690 4
        $tags = array();
691 4
        $this->caller->execute(TagCommand::getInstance($this)->listTags());
692 4
        foreach ($this->caller->getOutputLines() as $tagString) {
693 4
            if ($tagString != '') {
694 4
                $tags[] = new Tag($this, trim($tagString));
695
            }
696
        }
697
698 4
        return $tags;
699
    }
700
701
    /**
702
     * Return a tag object
703
     *
704
     * @param string $name The tag name
705
     *
706
     * @throws \RuntimeException
707
     * @throws \Symfony\Component\Process\Exception\RuntimeException
708
     * @return Tag|null
709
     */
710 28
    public function getTag($name)
711
    {
712 28
        $tagFinderOutput = $this->caller->execute(TagCommand::getInstance()->listTags())->getOutputLines(true);
713 28
        foreach ($tagFinderOutput as $line) {
714 28
            if ($line === $name) {
715 28
                return new Tag($this, $name);
716
            }
717
        }
718
719 1
        return null;
720
    }
721
722
    /**
723
     * Return the last created tag
724
     *
725
     * @throws \LogicException
726
     * @throws \RuntimeException
727
     * @throws \InvalidArgumentException
728
     * @return Tag|null
729
     */
730 1
    public function getLastTag()
731
    {
732 1
        $finder = Finder::create()
733 1
                  ->files()
734 1
                  ->in(sprintf('%s/.git/refs/tags', $this->path))
735 1
                  ->sortByChangedTime();
736 1
        if ($finder->count() == 0) {
737
            return null;
738
        }
739 1
        $files = iterator_to_array($finder->getIterator(), false);
740 1
        $files = array_reverse($files);
741
        /** @var $firstFile SplFileInfo */
742 1
        $firstFile = $files[0];
743 1
        $tagName = $firstFile->getFilename();
744
745 1
        return Tag::pick($this, $tagName);
746
    }
747
748
    /**
749
     * Try to get a branch or a tag by its name.
750
     *
751
     * @param string $name the reference name (a tag name or a branch name)
752
     *
753
     * @throws \RuntimeException
754
     * @throws \InvalidArgumentException
755
     * @throws \Symfony\Component\Process\Exception\RuntimeException
756
     * @return \GitElephant\Objects\Tag|\GitElephant\Objects\Branch|null
757
     */
758 1
    public function getBranchOrTag($name)
759
    {
760 1
        if (in_array($name, $this->getBranches(true))) {
761 1
            return new Branch($this, $name);
762
        }
763 1
        $tagFinderOutput = $this->caller->execute(TagCommand::getInstance($this)->listTags())->getOutputLines(true);
764 1
        foreach ($tagFinderOutput as $line) {
765 1
            if ($line === $name) {
766 1
                return new Tag($this, $name);
767
            }
768
        }
769
770 1
        return null;
771
    }
772
773
    /**
774
     * Return a Commit object
775
     *
776
     * @param string $ref The commit reference
777
     *
778
     * @throws \RuntimeException
779
     * @return Objects\Commit
780
     */
781 15
    public function getCommit($ref = 'HEAD')
782
    {
783 15
        $commit = Commit::pick($this, $ref);
784
785 15
        return $commit;
786
    }
787
788
    /**
789
     * count the commit to arrive to the given treeish
790
     *
791
     * @param string $start
792
     *
793
     * @throws \RuntimeException
794
     * @throws \Symfony\Component\Process\Exception\RuntimeException
795
     * @return int|void
796
     */
797 3
    public function countCommits($start = 'HEAD')
798
    {
799 3
        $commit = Commit::pick($this, $start);
800
801 3
        return $commit->count();
802
    }
803
804
    /**
805
     * Get a log for a ref
806
     *
807
     * @param string|TreeishInterface|array $ref         the treeish to check, as a string, as an object or as an array
808
     * @param string|Object                 $path        the physical path to the tree relative to the repository root
809
     * @param int|null                      $limit       limit to n entries
810
     * @param int|null                      $offset      skip n entries
811
     * @param boolean|false                 $firstParent skip commits brought in to branch by a merge
812
     *
813
     * @return \GitElephant\Objects\Log
814
     */
815 20
    public function getLog($ref = 'HEAD', $path = null, $limit = 10, $offset = null, $firstParent = false)
816
    {
817 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 815 can also be of type object<GitElephant\Objects\Object> or string; however, GitElephant\Objects\Log::__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...
Bug introduced by
It seems like $offset defined by parameter $offset on line 815 can also be of type integer; however, GitElephant\Objects\Log::__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...
818
    }
819
820
    /**
821
     * Get a log for a range ref
822
     *
823
     * @param string        $refStart
824
     * @param string        $refEnd
825
     * @param string|Object $path        the physical path to the tree relative to the repository root
826
     * @param int|null      $limit       limit to n entries
827
     * @param int|null      $offset      skip n entries
828
     * @param boolean|false $firstParent skip commits brought in to branch by a merge
829
     *
830
     * @return \GitElephant\Objects\LogRange
831
     */
832
    public function getLogRange($refStart, $refEnd, $path = null, $limit = 10, $offset = null, $firstParent = false)
833
    {
834
        // Handle when clients provide bad start reference on branch creation
835
        if (preg_match('~^[0]+$~', $refStart)) {
836
            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 832 can also be of type object<GitElephant\Objects\Object> or string; however, GitElephant\Objects\Log::__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...
Bug introduced by
It seems like $offset defined by parameter $offset on line 832 can also be of type integer; however, GitElephant\Objects\Log::__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...
Bug Best Practice introduced by
The return type of return new \GitElephant\...$offset, $firstParent); (GitElephant\Objects\Log) is incompatible with the return type documented by GitElephant\Repository::getLogRange of type GitElephant\Objects\LogRange.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
837
        }
838
839
        // Handle when clients provide bad end reference on branch deletion
840
        if (preg_match('~^[0]+$~', $refEnd)) {
841
            $refEnd = $refStart;
842
        }
843
844
        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 832 can also be of type object<GitElephant\Objects\Object> 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...
Bug introduced by
It seems like $offset defined by parameter $offset on line 832 can also be of type integer; 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...
845
    }
846
847
    /**
848
     * Get a log for an object
849
     *
850
     * @param \GitElephant\Objects\Object             $obj    The Object instance
851
     * @param null|string|\GitElephant\Objects\Branch $branch The branch to read from
852
     * @param int                                     $limit  Limit to n entries
853
     * @param int|null                                $offset Skip n entries
854
     *
855
     * @throws \RuntimeException
856
     * @throws \Symfony\Component\Process\Exception\LogicException
857
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
858
     * @throws \Symfony\Component\Process\Exception\RuntimeException
859
     * @return \GitElephant\Objects\Log
860
     */
861 3
    public function getObjectLog(Object $obj, $branch = null, $limit = 1, $offset = null)
862
    {
863 3
        $command = LogCommand::getInstance($this)->showObjectLog($obj, $branch, $limit, $offset);
864
865 3
        return Log::createFromOutputLines($this, $this->caller->execute($command)->getOutputLines());
866
    }
867
868
    /**
869
     * Checkout a branch
870
     * This function change the state of the repository on the filesystem
871
     *
872
     * @param string|TreeishInterface $ref    the reference to checkout
873
     * @param bool                    $create like -b on the command line
874
     *
875
     * @throws \RuntimeException
876
     * @throws \Symfony\Component\Process\Exception\LogicException
877
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
878
     * @throws \Symfony\Component\Process\Exception\RuntimeException
879
     * @return Repository
880
     */
881 24
    public function checkout($ref, $create = false)
882
    {
883 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 881 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...
884
            $this->createBranch($ref);
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 881 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...
885
        }
886 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 881 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...
887
888 24
        return $this;
889
    }
890
891
    /**
892
     * Retrieve an instance of Tree
893
     * Tree Object is Countable, Iterable and has ArrayAccess for easy manipulation
894
     *
895
     * @param string|TreeishInterface $ref  the treeish to check
896
     * @param string|Object           $path Object or null for root
897
     *
898
     * @throws \RuntimeException
899
     * @throws \Symfony\Component\Process\Exception\LogicException
900
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
901
     * @throws \Symfony\Component\Process\Exception\RuntimeException
902
     * @return Objects\Tree
903
     */
904 15
    public function getTree($ref = 'HEAD', $path = null)
905
    {
906 15
        if (is_string($path) && '' !== $path) {
907 9
            $outputLines = $this->getCaller()->execute(
908 9
                LsTreeCommand::getInstance($this)->tree($ref, $path)
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 904 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...
909 9
            )->getOutputLines(true);
910 9
            $path = TreeObject::createFromOutputLine($this, $outputLines[0]);
911
        }
912
913 15
        return new Tree($this, $ref, $path);
0 ignored issues
show
Bug introduced by
It seems like $ref defined by parameter $ref on line 904 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 904 can also be of type string; however, GitElephant\Objects\Tree::__construct() does only seem to accept object|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...
914
    }
915
916
    /**
917
     * Get a Diff object for a commit with its parent, by default the diff is between the current head and its parent
918
     *
919
     * @param \GitElephant\Objects\Commit|string      $commit1 A TreeishInterface instance
920
     * @param \GitElephant\Objects\Commit|string|null $commit2 A TreeishInterface instance
921
     * @param null|string|Object                      $path    The path to get the diff for or a Object instance
922
     *
923
     * @throws \RuntimeException
924
     * @throws \InvalidArgumentException
925
     * @return Objects\Diff\Diff
926
     */
927 2
    public function getDiff($commit1 = null, $commit2 = null, $path = null)
928
    {
929 2
        return Diff::create($this, $commit1, $commit2, $path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 927 can also be of type object<GitElephant\Objects\Object>; however, GitElephant\Objects\Diff\Diff::create() does only seem to accept null|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...
930
    }
931
932
    /**
933
     * Clone a repository
934
     *
935
     * @param string $url the repository url (i.e. git://github.com/matteosister/GitElephant.git)
936
     * @param null   $to  where to clone the repo
937
     *
938
     * @throws \RuntimeException
939
     * @throws \Symfony\Component\Process\Exception\LogicException
940
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
941
     * @throws \Symfony\Component\Process\Exception\RuntimeException
942
     * @return Repository
943
     */
944 2
    public function cloneFrom($url, $to = null)
945
    {
946 2
        $this->caller->execute(CloneCommand::getInstance($this)->cloneUrl($url, $to));
947
948 2
        return $this;
949
    }
950
951
    /**
952
     * @param string $name remote name
953
     * @param string $url  remote url
954
     *
955
     * @throws \RuntimeException
956
     * @throws \Symfony\Component\Process\Exception\LogicException
957
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
958
     * @throws \Symfony\Component\Process\Exception\RuntimeException
959
     * @return Repository
960
     */
961 7
    public function addRemote($name, $url)
962
    {
963 7
        $this->caller->execute(RemoteCommand::getInstance($this)->add($name, $url));
964
965 7
        return $this;
966
    }
967
968
    /**
969
     * @param string $name         remote name
970
     * @param bool   $queryRemotes Fetch new information from remotes
971
     *
972
     * @return \GitElephant\Objects\Remote
973
     */
974 1
    public function getRemote($name, $queryRemotes = true)
975
    {
976 1
        return Remote::pick($this, $name, $queryRemotes);
977
    }
978
979
    /**
980
     * gets a list of remote objects
981
     *
982
     * @param bool $queryRemotes Fetch new information from remotes
983
     *
984
     * @throws \RuntimeException
985
     * @throws \Symfony\Component\Process\Exception\LogicException
986
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
987
     * @throws \Symfony\Component\Process\Exception\RuntimeException
988
     * @return array
989
     */
990 1
    public function getRemotes($queryRemotes = true)
991
    {
992 1
        $remoteNames = $this->caller->execute(RemoteCommand::getInstance($this)->show(null, $queryRemotes))
993 1
          ->getOutputLines(true);
994 1
        $remotes = array();
995 1
        foreach ($remoteNames as $remoteName) {
996 1
            $remotes[] = $this->getRemote($remoteName, $queryRemotes);
997
        }
998
999 1
        return $remotes;
1000
    }
1001
1002
    /**
1003
     * Download objects and refs from another repository
1004
     *
1005
     * @param string $from
1006
     * @param string $ref
1007
     * @param bool   $tags
1008
     *
1009
     * @throws \RuntimeException
1010
     * @throws \Symfony\Component\Process\Exception\LogicException
1011
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
1012
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1013
     */
1014 1
    public function fetch($from = null, $ref = null, $tags = false)
1015
    {
1016 1
        $options = array();
1017 1
        if ($tags === true) {
1018 1
            $options = array('--tags');
1019
        }
1020 1
        $this->caller->execute(FetchCommand::getInstance($this)->fetch($from, $ref, $options));
1021 1
    }
1022
1023
    /**
1024
     * Fetch from and merge with another repository or a local branch
1025
     *
1026
     * @param string $from
1027
     * @param string $ref
1028
     * @param bool   $rebase
1029
     * @throws \RuntimeException
1030
     * @throws \Symfony\Component\Process\Exception\LogicException
1031
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
1032
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1033
     */
1034 2
    public function pull($from = null, $ref = null, $rebase = true)
1035
    {
1036 2
        $this->caller->execute(PullCommand::getInstance($this)->pull($from, $ref, $rebase));
1037 2
    }
1038
1039
    /**
1040
     * Push changes to remote repository
1041
     *
1042
     * @param string $to
1043
     * @param string $ref
1044
     * @throws \RuntimeException
1045
     * @throws \Symfony\Component\Process\Exception\LogicException
1046
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
1047
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1048
     */
1049 1
    public function push($to = null, $ref = null)
1050
    {
1051 1
        $this->caller->execute(PushCommand::getInstance($this)->push($to, $ref));
1052 1
    }
1053
1054
    /**
1055
     * get the humanish name of the repository
1056
     *
1057
     * @return string
1058
     */
1059 2
    public function getHumanishName()
1060
    {
1061 2
        $name = substr($this->getPath(), strrpos($this->getPath(), '/') + 1);
1062 2
        $name = str_replace('.git', '.', $name);
1063 2
        $name = str_replace('.bundle', '.', $name);
1064
1065 2
        return $name;
1066
    }
1067
1068
    /**
1069
     * output a node content as an array of lines
1070
     *
1071
     * @param \GitElephant\Objects\Object                  $obj     The Object of type BLOB
1072
     * @param \GitElephant\Objects\TreeishInterface|string $treeish A treeish object
1073
     *
1074
     * @throws \RuntimeException
1075
     * @throws \Symfony\Component\Process\Exception\LogicException
1076
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
1077
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1078
     * @return array
1079
     */
1080 1
    public function outputContent(Object $obj, $treeish)
1081
    {
1082 1
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1083
1084 1
        return $this->caller->execute($command)->getOutputLines();
1085
    }
1086
1087
    /**
1088
     * output a node raw content
1089
     *
1090
     * @param \GitElephant\Objects\Object                  $obj     The Object of type BLOB
1091
     * @param \GitElephant\Objects\TreeishInterface|string $treeish A treeish object
1092
     *
1093
     * @throws \RuntimeException
1094
     * @throws \Symfony\Component\Process\Exception\LogicException
1095
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
1096
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1097
     * @return string
1098
     */
1099
    public function outputRawContent(Object $obj, $treeish)
1100
    {
1101
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1102
1103
        return $this->caller->execute($command)->getRawOutput();
1104
    }
1105
1106
    /**
1107
     * Get the path
1108
     *
1109
     * @return string
1110
     */
1111 63
    public function getPath()
1112
    {
1113 63
        return $this->path;
1114
    }
1115
1116
    /**
1117
     * Get the repository name
1118
     *
1119
     * @return string
1120
     */
1121 1
    public function getName()
1122
    {
1123 1
        return $this->name;
1124
    }
1125
1126
    /**
1127
     * Set the repository name
1128
     *
1129
     * @param string $name the repository name
1130
     */
1131 1
    public function setName($name)
1132
    {
1133 1
        $this->name = $name;
1134 1
    }
1135
1136
    /**
1137
     * Caller setter
1138
     *
1139
     * @param \GitElephant\Command\Caller\Caller $caller the caller variable
1140
     */
1141
    public function setCaller($caller)
1142
    {
1143
        $this->caller = $caller;
1144
    }
1145
1146
    /**
1147
     * Caller getter
1148
     *
1149
     * @return \GitElephant\Command\Caller\Caller
1150
     */
1151 89
    public function getCaller()
1152
    {
1153 89
        return $this->caller;
1154
    }
1155
1156
    /**
1157
     * get global config list
1158
     *
1159
     * @return array Global config list
1160
     */
1161 95
    public function getGlobalConfigs()
1162
    {
1163 95
        return $this->globalConfigs;
1164
    }
1165
1166
    /**
1167
     * add a key/value pair to the global config list
1168
     *
1169
     * @param string $name  The config name
1170
     * @param string $value The config value
1171
     */
1172 1
    public function addGlobalConfig($name, $value)
1173
    {
1174 1
        $this->globalConfigs[$name] = $value;
1175 1
    }
1176
1177
    /**
1178
     * remove an element form the global config list, identified by key
1179
     *
1180
     * @param  string $name The config name
1181
     */
1182 1
    public function removeGlobalConfig($name)
1183
    {
1184 1
        if (isset($this->globalConfigs[$name])) {
1185 1
            unset($this->globalConfigs[$name]);
1186
        }
1187 1
    }
1188
1189
    /**
1190
     * get global options list
1191
     *
1192
     * @return array Global options list
1193
     */
1194 95
    public function getGlobalOptions()
1195
    {
1196 95
        return $this->globalOptions;
1197
    }
1198
1199
    /**
1200
     * add a key/value pair to the global option list
1201
     *
1202
     * @param string $name  The option name
1203
     * @param string $value The option value
1204
     */
1205 1
    public function addGlobalOption($name, $value)
1206
    {
1207 1
        $this->globalOptions[$name] = $value;
1208 1
    }
1209
1210
    /**
1211
     * remove an element form the global option list, identified by key
1212
     *
1213
     * @param  string $name The option name
1214
     */
1215 1
    public function removeGlobalOption($name)
1216
    {
1217 1
        if (isset($this->globalOptions[$name])) {
1218 1
            unset($this->globalOptions[$name]);
1219
        }
1220 1
    }
1221
1222
    /**
1223
     * get global command arguments list
1224
     *
1225
     * @return array Global command arguments list
1226
     */
1227 95
    public function getGlobalCommandArguments()
1228
    {
1229 95
        return $this->globalCommandArguments;
1230
    }
1231
1232
    /**
1233
     * add a value to the global command argument list
1234
     *
1235
     * @param string $value The command argument
1236
     */
1237 1
    public function addGlobalCommandArgument($value)
1238
    {
1239 1
        if (!in_array($value, $this->globalCommandArguments, true)) {
1240 1
            $this->globalCommandArguments[] = $value;
1241
        }
1242 1
    }
1243
1244
    /**
1245
     * remove an element form the global command argument list, identified by
1246
     * value
1247
     *
1248
     * @param  string $value The command argument
1249
     */
1250 1
    public function removeGlobalCommandArgument($value)
1251
    {
1252 1
        if (in_array($value, $this->globalCommandArguments, true)) {
1253 1
            $index = array_search($value, $this->globalCommandArguments);
1254 1
            unset($this->globalCommandArguments[$index]);
1255
        }
1256 1
    }
1257
1258
    /**
1259
     *  Save your local modifications to a new stash, and run git reset --hard to revert them.
1260
     *
1261
     * @param string|null $message
1262
     * @param boolean $includeUntracked
1263
     * @param boolean $keepIndex
1264
     */
1265 2
    public function stash($message = null, $includeUntracked = false, $keepIndex = false)
1266
    {
1267 2
        $command = (StashCommand::getInstance($this))->save($message, $includeUntracked, $keepIndex);
1268 2
        $this->caller->execute($command);
1269 1
    }
1270
1271
    /**
1272
     * Shows stash list
1273
     *
1274
     * @param array|null $options
1275
     *
1276
     * @return array
1277
     */
1278 1
    public function stashList(array $options = null)
1279
    {
1280 1
        $command = (StashCommand::getInstance($this))->list($options);
1281 1
        $this->caller->execute($command);
1282 1
        return array_map('trim', $this->caller->getOutputLines(true));
1283
    }
1284
1285
    /**
1286
     * Shows details for a stash
1287
     *
1288
     * @param string $stash
1289
     *
1290
     * @return string
1291
     */
1292 1
    public function stashShow($stash)
1293
    {
1294 1
        $command = (StashCommand::getInstance($this))->show($stash);
1295 1
        $this->caller->execute($command);
1296 1
        return $this->caller->getOutput();
1297
    }
1298
1299
    /**
1300
     * Drops a stash
1301
     *
1302
     * @param string $stash
1303
     */
1304 1
    public function stashDrop($stash)
1305
    {
1306 1
        $command = (StashCommand::getInstance($this))->drop($stash);
1307 1
        $this->caller->execute($command);
1308 1
    }
1309
1310
    /**
1311
     * Applies a stash
1312
     *
1313
     * @param string $stash
1314
     * @param boolean $index
1315
     */
1316 1
    public function stashApply($stash, $index = false)
1317
    {
1318 1
        $command = (StashCommand::getInstance($this))->apply($stash, $index);
1319 1
        $this->caller->execute($command);
1320 1
    }
1321
1322
    /**
1323
     *  Applies a stash, then removes it from the stash
1324
     *
1325
     * @param string $stash
1326
     * @param boolean $index
1327
     */
1328 1
    public function stashPop($stash, $index = false)
1329
    {
1330 1
        $command = (StashCommand::getInstance($this))->pop($stash, $index);
1331 1
        $this->caller->execute($command);
1332 1
    }
1333
1334
    /**
1335
     *  Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created
1336
     *
1337
     * @param string $branch
1338
     * @param string $stash
1339
     */
1340 1
    public function stashBranch($branch, $stash)
1341
    {
1342 1
        $command = (StashCommand::getInstance($this))->branch($branch, $stash);
1343 1
        $this->caller->execute($command);
1344 1
    }
1345
1346
    /**
1347
     *  Save your local modifications to a new stash, and run git reset --hard to revert them.
1348
     *
1349
     */
1350
    public function stashClear()
1351
    {
1352
        $command = (StashCommand::getInstance($this))->clear();
1353
        $this->caller->execute($command);
1354
    }
1355
1356
    /**
1357
     *  Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the
1358
     *  ref namespace.
1359
     *
1360
     * @return string
1361
     */
1362 1
    public function stashCreate()
1363
    {
1364 1
        $command = (StashCommand::getInstance($this))->clear();
1365 1
        $this->caller->execute($command);
1366 1
        return $this->caller->getOutput();
1367
    }
1368
}
1369