Completed
Push — develop ( 2eea19...05bbe8 )
by
unknown
13:03
created

Repository::stash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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