Completed
Pull Request — develop (#132)
by
unknown
02:58
created

Repository::stashBranch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 * GitElephant - An abstraction layer for git written in PHP
4
 * Copyright (C) 2013  Matteo Giachino
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see [http://www.gnu.org/licenses/].
18
 */
19
20
namespace GitElephant;
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
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
     *
119
     * @throws Exception\InvalidRepositoryPathException
120
     */
121 105
    public function __construct($repositoryPath, GitBinary $binary = null, $name = null)
122
    {
123 105
        if (is_null($binary)) {
124 105
            $binary = new GitBinary();
125
        }
126
127 105
        $this->path = $repositoryPath;
128 105
        $this->caller = new Caller($binary, $repositoryPath);
129 105
        $this->name = $name;
130 105
    }
131
132
    /**
133
     * Factory method
134
     *
135
     * @param string         $repositoryPath the path of the git repository
136
     * @param GitBinary|null $binary         the GitBinary instance that calls the commands
137
     * @param string         $name           a repository name
138
     *
139
     * @return \GitElephant\Repository
140
     */
141 104
    public static function open($repositoryPath, GitBinary $binary = null, $name = null)
142
    {
143 104
        return new self($repositoryPath, $binary, $name);
144
    }
145
146
    /**
147
     * create a repository from a remote git url, or a local filesystem
148
     * and save it in a temp folder
149
     *
150
     * @param string|Repository $git            the git remote url, or the filesystem path
151
     * @param null              $repositoryPath path
152
     * @param GitBinary         $binary         binary
153
     * @param null              $name           repository name
154
     *
155
     * @throws \RuntimeException
156
     * @throws \Symfony\Component\Filesystem\Exception\IOException
157
     * @return Repository
158
     */
159 1
    public static function createFromRemote($git, $repositoryPath = null, GitBinary $binary = null, $name = null)
160
    {
161 1
        if (null === $repositoryPath) {
162 1
            $tempDir = realpath(sys_get_temp_dir());
163 1
            $repositoryPath = sprintf('%s%s%s', $tempDir, DIRECTORY_SEPARATOR, sha1(uniqid()));
164 1
            $fs = new Filesystem();
165 1
            $fs->mkdir($repositoryPath);
166
        }
167 1
        $repository = new Repository($repositoryPath, $binary, $name);
168 1
        if ($git instanceof Repository) {
169
            $git = $git->getPath();
170
        }
171 1
        $repository->cloneFrom($git, $repositoryPath);
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 1
        $repository->checkoutAllRemoteBranches();
173
174 1
        return $repository;
175
    }
176
177
    /**
178
     * Init the repository
179
     *
180
     * @param bool $bare created a bare repository
181
     *
182
     * @throws \RuntimeException
183
     * @throws \Symfony\Component\Process\Exception\LogicException
184
     * @throws InvalidArgumentException
185
     * @throws \Symfony\Component\Process\Exception\RuntimeException
186
     * @return Repository
187
     */
188 94
    public function init($bare = false)
189
    {
190 94
        $this->caller->execute(MainCommand::getInstance($this)->init($bare));
191
192 94
        return $this;
193
    }
194
195
    /**
196
     * Stage the working tree content
197
     *
198
     * @param string|Object $path the path to store
199
     *
200
     * @throws \RuntimeException
201
     * @throws \Symfony\Component\Process\Exception\LogicException
202
     * @throws InvalidArgumentException
203
     * @throws \Symfony\Component\Process\Exception\RuntimeException
204
     * @return Repository
205
     */
206 90
    public function stage($path = '.')
207
    {
208 90
        $this->caller->execute(MainCommand::getInstance($this)->add($path));
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 206 can also be of type object<GitElephant\Objects\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...
209
210 90
        return $this;
211
    }
212
213
    /**
214
     * Unstage a tree content
215
     *
216
     * @param string|Object $path the path to unstage
217
     *
218
     * @throws \RuntimeException
219
     * @throws \Symfony\Component\Process\Exception\LogicException
220
     * @throws InvalidArgumentException
221
     * @throws \Symfony\Component\Process\Exception\RuntimeException
222
     * @return Repository
223
     */
224 2
    public function unstage($path)
225
    {
226 2
        $this->caller->execute(MainCommand::getInstance($this)->unstage($path), true, null, 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\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...
227
228 2
        return $this;
229
    }
230
231
    /**
232
     * Move a file/directory
233
     *
234
     * @param string|Object $from source path
235
     * @param string|Object $to   destination path
236
     *
237
     * @throws \RuntimeException
238
     * @throws \Symfony\Component\Process\Exception\LogicException
239
     * @throws \InvalidArgumentException
240
     * @throws InvalidArgumentException
241
     * @throws \Symfony\Component\Process\Exception\RuntimeException
242
     * @return Repository
243
     */
244 1
    public function move($from, $to)
245
    {
246 1
        $this->caller->execute(MainCommand::getInstance($this)->move($from, $to));
247
248 1
        return $this;
249
    }
250
251
    /**
252
     * Remove a file/directory
253
     *
254
     * @param string|Object $path      the path to remove
255
     * @param bool          $recursive recurse
256
     * @param bool          $force     force
257
     *
258
     * @throws \RuntimeException
259
     * @throws \Symfony\Component\Process\Exception\LogicException
260
     * @throws \InvalidArgumentException
261
     * @throws InvalidArgumentException
262
     * @throws \Symfony\Component\Process\Exception\RuntimeException
263
     * @return Repository
264
     */
265 1
    public function remove($path, $recursive = false, $force = false)
266
    {
267 1
        $this->caller->execute(MainCommand::getInstance($this)->remove($path, $recursive, $force));
268
269 1
        return $this;
270
    }
271
272
    /**
273
     * Commit content to the repository, eventually staging all unstaged content
274
     *
275
     * @param string        $message    the commit message
276
     * @param bool          $stageAll   whether to stage on not everything before commit
277
     * @param string|null   $ref        the reference to commit to (checkout -> commit -> checkout previous)
278
     * @param string|Author $author     override the author for this commit
279
     * @param bool          $allowEmpty override the author for this commit
280
     *
281
     * @throws \RuntimeException
282
     * @throws \InvalidArgumentException
283
     * @throws \Symfony\Component\Process\Exception\RuntimeException
284
     * @return Repository
285
     */
286 85
    public function commit($message, $stageAll = false, $ref = null, $author = null, $allowEmpty = false)
287
    {
288 85
        $currentBranch = null;
289 85
        if (! is_null($ref)) {
290 1
            $currentBranch = $this->getMainBranch();
291 1
            $this->checkout($ref);
292
        }
293 85
        if ($stageAll) {
294 83
            $this->stage();
295
        }
296 85
        $this->caller->execute(MainCommand::getInstance($this)->commit($message, $stageAll, $author, $allowEmpty));
297 85
        if (! is_null($ref)) {
298 1
            $this->checkout($currentBranch);
0 ignored issues
show
Bug introduced by
It seems like $currentBranch defined by null on line 288 can be null; however, GitElephant\Repository::checkout() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
299
        }
300
301 85
        return $this;
302
    }
303
304
    /**
305
     * rev-parse command - often used to return a commit tag.
306
     *
307
     * @param array                  $options the options to apply to rev-parse
308
     * @param string|Object|Commit   $arg the argument (may be a branch head, etc)
309
     *
310
     * @throws \RuntimeException
311
     * @throws \InvalidArgumentException
312
     * @throws \Symfony\Component\Process\Exception\RuntimeException
313
     * @return array
314
     */
315 1
    public function revParse($arg = null, array $options = array())
316
    {
317 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 315 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...
318
319 1
        return array_map('trim', $this->caller->getOutputLines(true));
320
    }
321
322
    /**
323
     * Check if this is a bare repository
324
     * @return boolean
325
     */
326 1
    public function isBare()
327
    {
328 1
        $options = array(RevParseCommand::OPTION_IS_BARE_REPOSIORY);
329 1
        $this->caller->execute(RevParseCommand::getInstance()->revParse(null, $options));
330
331 1
        return trim($this->caller->getOutput()) === 'true';
332
    }
333
334
    /**
335
     * @param TreeishInterface|Commit|string $arg
336
     * @param array $options
337
     */
338 2
    public function reset($arg, $options)
339
    {
340 2
        $this->caller->execute(ResetCommand::getInstance($this)->reset($arg,$options));
341 2
    }
342
343
    /**
344
     * Get the repository status
345
     *
346
     * @return Status
347
     */
348 5
    public function getStatus()
349
    {
350 5
        return Status::get($this);
351
    }
352
353
    /**
354
     * @return Status
355
     */
356 1
    public function getWorkingTreeStatus()
357
    {
358 1
        return StatusWorkingTree::get($this);
359
    }
360
361
    /**
362
     * @return Status
363
     */
364 4
    public function getIndexStatus()
365
    {
366 4
        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
     * @throws InvalidArgumentException
395
     * @throws \Symfony\Component\Process\Exception\RuntimeException
396
     * @return array
397
     */
398 4
    public function getStatusOutput()
399
    {
400 4
        $this->caller->execute(MainCommand::getInstance($this)->status());
401
402 4
        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
     * @throws \RuntimeException
412
     * @throws \Symfony\Component\Process\Exception\RuntimeException
413
     * @return Repository
414
     */
415 27
    public function createBranch($name, $startPoint = null)
416
    {
417 27
        Branch::create($this, $name, $startPoint);
418
419 27
        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
     * @throws InvalidArgumentException
432
     * @throws \Symfony\Component\Process\Exception\RuntimeException
433
     * @return Repository
434
     */
435 1
    public function deleteBranch($name, $force = false)
436
    {
437 1
        $this->caller->execute(BranchCommand::getInstance($this)->delete($name, $force));
438
439 1
        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
     * @throws \InvalidArgumentException
452
     * @throws \Symfony\Component\Process\Exception\RuntimeException
453
     * @return array
454
     */
455 17
    public function getBranches($namesOnly = false, $all = false)
456
    {
457 17
        $branches = array();
458 17
        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 6
                    return ltrim($v, '* ');
465 6
                },
466 6
                $outputLines
467
            );
468
        } else {
469 14
            $outputLines = $this->caller->execute(
470 14
                BranchCommand::getInstance($this)->listBranches($all)
471 14
            )->getOutputLines(true);
472 14
            foreach ($outputLines as $branchLine) {
473 14
                $branches[] = Branch::createFromOutputLine($this, $branchLine);
474
            }
475
        }
476
477 17
        return $branches;
478
    }
479
480
    /**
481
     * Return the actually checked out branch
482
     *
483
     * @throws \RuntimeException
484
     * @throws \InvalidArgumentException
485
     * @throws \Symfony\Component\Process\Exception\RuntimeException
486
     * @return Objects\Branch
487
     */
488 5
    public function getMainBranch()
489
    {
490 5
        $filtered = array_filter(
491 5
            $this->getBranches(),
492
            function (Branch $branch) {
493 5
                return $branch->getCurrent();
494 5
            }
495
        );
496 5
        sort($filtered);
497
498 5
        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
     * @throws \InvalidArgumentException
508
     * @throws \Symfony\Component\Process\Exception\RuntimeException
509
     * @return null|Branch
510
     */
511 9
    public function getBranch($name)
512
    {
513
        /** @var Branch $branch */
514 9
        foreach ($this->getBranches() as $branch) {
515 9
            if ($branch->getName() == $name) {
516 9
                return $branch;
517
            }
518
        }
519
520 1
        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
     * @throws \InvalidArgumentException
530
     * @throws \Symfony\Component\Process\Exception\RuntimeException
531
     * @return Repository
532
     */
533 1
    public function checkoutAllRemoteBranches($remote = 'origin')
534
    {
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 1
                return !in_array($branch, $actualBranches)
542 1
                && preg_match('/^remotes(.+)$/', $branch)
543 1
                && !preg_match('/^(.+)(HEAD)(.*?)$/', $branch);
544 1
            }
545
        );
546 1
        foreach ($realBranches as $realBranch) {
547 1
            $this->checkout(str_replace(sprintf('remotes/%s/', $remote), '', $realBranch));
548
        }
549 1
        $this->checkout($actualBranch);
550
551 1
        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
     * @throws InvalidArgumentException
564
     * @throws \Symfony\Component\Process\Exception\RuntimeException
565
     * @return Repository
566
     */
567 2
    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
        }
577
578 2
        $options = array();
579
        switch ($mode) {
580 2
            case 'ff-only':
581 1
                $options[] = MergeCommand::MERGE_OPTION_FF_ONLY;
582 1
                break;
583 2
            case 'no-ff':
584 1
                $options[] = MergeCommand::MERGE_OPTION_NO_FF;
585 1
                break;
586
        }
587
588 2
        $this->caller->execute(MergeCommand::getInstance($this)->merge($branch, $message, $options));
589
590 2
        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
     * @throws \RuntimeException
602
     * @throws \Symfony\Component\Process\Exception\RuntimeException
603
     * @return Repository
604
     */
605 25
    public function createTag($name, $startPoint = null, $message = null)
606
    {
607 25
        Tag::create($this, $name, $startPoint, $message);
608
609 25
        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
     * @throws \RuntimeException
619
     * @throws \Symfony\Component\Process\Exception\RuntimeException
620
     * @return Repository
621
     */
622 2
    public function deleteTag($tag)
623
    {
624 2
        if ($tag instanceof Tag) {
625 1
            $tag->delete();
626
        } else {
627 1
            Tag::pick($this, $tag)->delete();
628
        }
629
630 2
        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
     * @throws InvalidArgumentException
642
     * @throws \Symfony\Component\Process\Exception\RuntimeException
643
     * @return Repository
644
     */
645 1
    public function addSubmodule($gitUrl, $path = null)
646
    {
647 1
        $this->caller->execute(SubmoduleCommand::getInstance($this)->add($gitUrl, $path));
648
649 1
        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
     * @throws InvalidArgumentException
687
     * @throws \Symfony\Component\Process\Exception\RuntimeException
688
     * @return array
689
     */
690 4
    public function getTags()
691
    {
692 4
        $tags = array();
693 4
        $this->caller->execute(TagCommand::getInstance($this)->listTags());
694 4
        foreach ($this->caller->getOutputLines() as $tagString) {
695 4
            if ($tagString != '') {
696 4
                $tags[] = new Tag($this, trim($tagString));
697
            }
698
        }
699
700 4
        return $tags;
701
    }
702
703
    /**
704
     * Return a tag object
705
     *
706
     * @param string $name The tag name
707
     *
708
     * @throws \RuntimeException
709
     * @throws \Symfony\Component\Process\Exception\RuntimeException
710
     * @return Tag|null
711
     */
712 28
    public function getTag($name)
713
    {
714 28
        $tagFinderOutput = $this->caller->execute(TagCommand::getInstance()->listTags())->getOutputLines(true);
715 28
        foreach ($tagFinderOutput as $line) {
716 28
            if ($line === $name) {
717 28
                return new Tag($this, $name);
718
            }
719
        }
720
721 1
        return null;
722
    }
723
724
    /**
725
     * Return the last created tag
726
     *
727
     * @throws \LogicException
728
     * @throws \RuntimeException
729
     * @throws \InvalidArgumentException
730
     * @return Tag|null
731
     */
732 1
    public function getLastTag()
733
    {
734 1
        $finder = Finder::create()
735 1
                  ->files()
736 1
                  ->in(sprintf('%s/.git/refs/tags', $this->path))
737 1
                  ->sortByChangedTime();
738 1
        if ($finder->count() == 0) {
739
            return null;
740
        }
741 1
        $files = iterator_to_array($finder->getIterator(), false);
742 1
        $files = array_reverse($files);
743
        /** @var $firstFile SplFileInfo */
744 1
        $firstFile = $files[0];
745 1
        $tagName = $firstFile->getFilename();
746
747 1
        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
     * @throws \InvalidArgumentException
757
     * @throws \Symfony\Component\Process\Exception\RuntimeException
758
     * @return \GitElephant\Objects\Tag|\GitElephant\Objects\Branch|null
759
     */
760 1
    public function getBranchOrTag($name)
761
    {
762 1
        if (in_array($name, $this->getBranches(true))) {
763 1
            return new Branch($this, $name);
764
        }
765 1
        $tagFinderOutput = $this->caller->execute(TagCommand::getInstance($this)->listTags())->getOutputLines(true);
766 1
        foreach ($tagFinderOutput as $line) {
767 1
            if ($line === $name) {
768 1
                return new Tag($this, $name);
769
            }
770
        }
771
772 1
        return null;
773
    }
774
775
    /**
776
     * Return a Commit object
777
     *
778
     * @param string $ref The commit reference
779
     *
780
     * @throws \RuntimeException
781
     * @return Objects\Commit
782
     */
783 15
    public function getCommit($ref = 'HEAD')
784
    {
785 15
        $commit = Commit::pick($this, $ref);
786
787 15
        return $commit;
788
    }
789
790
    /**
791
     * count the commit to arrive to the given treeish
792
     *
793
     * @param string $start
794
     *
795
     * @throws \RuntimeException
796
     * @throws \Symfony\Component\Process\Exception\RuntimeException
797
     * @return int
798
     */
799 3
    public function countCommits($start = 'HEAD')
800
    {
801 3
        $commit = Commit::pick($this, $start);
802
803 3
        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|Object                 $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
     * @param boolean|false                 $firstParent skip commits brought in to branch by a merge
814
     *
815
     * @return \GitElephant\Objects\Log
816
     */
817 20
    public function getLog($ref = 'HEAD', $path = null, $limit = 10, $offset = null, $firstParent = false)
818
    {
819 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 817 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 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...
820
    }
821
822
    /**
823
     * Get a log for a range ref
824
     *
825
     * @param string        $refStart
826
     * @param string        $refEnd
827
     * @param string|Object $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 $path defined by parameter $path on line 834 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 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...
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 $path defined by parameter $path on line 834 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 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...
847
    }
848
849
    /**
850
     * Get a log for an object
851
     *
852
     * @param \GitElephant\Objects\Object             $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
     * @throws InvalidArgumentException
860
     * @throws \Symfony\Component\Process\Exception\RuntimeException
861
     * @return \GitElephant\Objects\Log
862
     */
863 3
    public function getObjectLog(\GitElephant\Objects\Object $obj, $branch = null, $limit = 1, $offset = null)
864
    {
865 3
        $command = LogCommand::getInstance($this)->showObjectLog($obj, $branch, $limit, $offset);
866
867 3
        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
     * @throws InvalidArgumentException
880
     * @throws \Symfony\Component\Process\Exception\RuntimeException
881
     * @return Repository
882
     */
883 24
    public function checkout($ref, $create = false)
884
    {
885 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 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
            $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 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 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 24
        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|Object           $path Object or null for root
899
     *
900
     * @throws \RuntimeException
901
     * @throws \Symfony\Component\Process\Exception\LogicException
902
     * @throws InvalidArgumentException
903
     * @throws \Symfony\Component\Process\Exception\RuntimeException
904
     * @return Objects\Tree
905
     */
906 15
    public function getTree($ref = 'HEAD', $path = null)
907
    {
908 15
        if (is_string($path) && '' !== $path) {
909 9
            $outputLines = $this->getCaller()->execute(
910 9
                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 9
            )->getOutputLines(true);
912 9
            $path = TreeObject::createFromOutputLine($this, $outputLines[0]);
913
        }
914
915 15
        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|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|Object                      $path    The path to get the diff for or a Object instance
924
     *
925
     * @throws \RuntimeException
926
     * @throws \InvalidArgumentException
927
     * @return Objects\Diff\Diff
928
     */
929 2
    public function getDiff($commit1 = null, $commit2 = null, $path = null)
930
    {
931 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 929 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...
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
     * @throws InvalidArgumentException
943
     * @throws \Symfony\Component\Process\Exception\RuntimeException
944
     * @return Repository
945
     */
946 2
    public function cloneFrom($url, $to = null)
947
    {
948 2
        $this->caller->execute(CloneCommand::getInstance($this)->cloneUrl($url, $to));
949
950 2
        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
     * @throws InvalidArgumentException
960
     * @throws \Symfony\Component\Process\Exception\RuntimeException
961
     * @return Repository
962
     */
963 7
    public function addRemote($name, $url)
964
    {
965 7
        $this->caller->execute(RemoteCommand::getInstance($this)->add($name, $url));
966
967 7
        return $this;
968
    }
969
970
    /**
971
     * @param string $name         remote name
972
     * @param bool   $queryRemotes Fetch new information from remotes
973
     *
974
     * @return \GitElephant\Objects\Remote
975
     */
976 1
    public function getRemote($name, $queryRemotes = true)
977
    {
978 1
        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
     * @throws InvalidArgumentException
989
     * @throws \Symfony\Component\Process\Exception\RuntimeException
990
     * @return array
991
     */
992 1
    public function getRemotes($queryRemotes = true)
993
    {
994 1
        $remoteNames = $this->caller->execute(RemoteCommand::getInstance($this)->show(null, $queryRemotes))
995 1
          ->getOutputLines(true);
996 1
        $remotes = array();
997 1
        foreach ($remoteNames as $remoteName) {
998 1
            $remotes[] = $this->getRemote($remoteName, $queryRemotes);
999
        }
1000
1001 1
        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
     * @throws \Symfony\Component\Process\Exception\LogicException
1013
     * @throws InvalidArgumentException
1014
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1015
     */
1016 1
    public function fetch($from = null, $ref = null, $tags = false)
1017
    {
1018 1
        $options = array();
1019 1
        if ($tags === true) {
1020 1
            $options = array('--tags');
1021
        }
1022 1
        $this->caller->execute(FetchCommand::getInstance($this)->fetch($from, $ref, $options));
1023 1
    }
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
     * @throws \Symfony\Component\Process\Exception\LogicException
1033
     * @throws InvalidArgumentException
1034
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1035
     */
1036 2
    public function pull($from = null, $ref = null, $rebase = true)
1037
    {
1038 2
        $this->caller->execute(PullCommand::getInstance($this)->pull($from, $ref, $rebase));
1039 2
    }
1040
1041
    /**
1042
     * Push changes to remote repository
1043
     *
1044
     * @param string $to
1045
     * @param string $ref
1046
     * @throws \RuntimeException
1047
     * @throws \Symfony\Component\Process\Exception\LogicException
1048
     * @throws InvalidArgumentException
1049
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1050
     */
1051 1
    public function push($to = null, $ref = null)
1052
    {
1053 1
        $this->caller->execute(PushCommand::getInstance($this)->push($to, $ref));
1054 1
    }
1055
1056
    /**
1057
     * get the humanish name of the repository
1058
     *
1059
     * @return string
1060
     */
1061 2
    public function getHumanishName()
1062
    {
1063 2
        $name = substr($this->getPath(), strrpos($this->getPath(), '/') + 1);
1064 2
        $name = str_replace('.git', '.', $name);
1065 2
        $name = str_replace('.bundle', '.', $name);
1066
1067 2
        return $name;
1068
    }
1069
1070
    /**
1071
     * output a node content as an array of lines
1072
     *
1073
     * @param \GitElephant\Objects\Object                  $obj     The Object of type BLOB
1074
     * @param \GitElephant\Objects\TreeishInterface|string $treeish A treeish object
1075
     *
1076
     * @throws \RuntimeException
1077
     * @throws \Symfony\Component\Process\Exception\LogicException
1078
     * @throws InvalidArgumentException
1079
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1080
     * @return array
1081
     */
1082 1
    public function outputContent(\GitElephant\Objects\Object $obj, $treeish)
1083
    {
1084 1
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1085
1086 1
        return $this->caller->execute($command)->getOutputLines();
1087
    }
1088
1089
    /**
1090
     * output a node raw content
1091
     *
1092
     * @param \GitElephant\Objects\Object                  $obj     The Object of type BLOB
1093
     * @param \GitElephant\Objects\TreeishInterface|string $treeish A treeish object
1094
     *
1095
     * @throws \RuntimeException
1096
     * @throws \Symfony\Component\Process\Exception\LogicException
1097
     * @throws InvalidArgumentException
1098
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1099
     * @return string
1100
     */
1101
    public function outputRawContent(\GitElephant\Objects\Object $obj, $treeish)
1102
    {
1103
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1104
1105
        return $this->caller->execute($command)->getRawOutput();
1106
    }
1107
1108
    /**
1109
     * Get the path
1110
     *
1111
     * @return string
1112
     */
1113 63
    public function getPath()
1114
    {
1115 63
        return $this->path;
1116
    }
1117
1118
    /**
1119
     * Get the repository name
1120
     *
1121
     * @return string
1122
     */
1123 1
    public function getName()
1124
    {
1125 1
        return $this->name;
1126
    }
1127
1128
    /**
1129
     * Set the repository name
1130
     *
1131
     * @param string $name the repository name
1132
     */
1133 1
    public function setName($name)
1134
    {
1135 1
        $this->name = $name;
1136 1
    }
1137
1138
    /**
1139
     * Caller setter
1140
     *
1141
     * @param \GitElephant\Command\Caller\Caller $caller the caller variable
1142
     */
1143
    public function setCaller($caller)
1144
    {
1145
        $this->caller = $caller;
1146
    }
1147
1148
    /**
1149
     * Caller getter
1150
     *
1151
     * @return \GitElephant\Command\Caller\Caller
1152
     */
1153 89
    public function getCaller()
1154
    {
1155 89
        return $this->caller;
1156
    }
1157
1158
    /**
1159
     * get global config list
1160
     *
1161
     * @return array Global config list
1162
     */
1163 95
    public function getGlobalConfigs()
1164
    {
1165 95
        return $this->globalConfigs;
1166
    }
1167
1168
    /**
1169
     * add a key/value pair to the global config list
1170
     *
1171
     * @param string $name  The config name
1172
     * @param string $value The config value
1173
     */
1174 1
    public function addGlobalConfig($name, $value)
1175
    {
1176 1
        $this->globalConfigs[$name] = $value;
1177 1
    }
1178
1179
    /**
1180
     * remove an element form the global config list, identified by key
1181
     *
1182
     * @param  string $name The config name
1183
     */
1184 1
    public function removeGlobalConfig($name)
1185
    {
1186 1
        if (isset($this->globalConfigs[$name])) {
1187 1
            unset($this->globalConfigs[$name]);
1188
        }
1189 1
    }
1190
1191
    /**
1192
     * get global options list
1193
     *
1194
     * @return array Global options list
1195
     */
1196 95
    public function getGlobalOptions()
1197
    {
1198 95
        return $this->globalOptions;
1199
    }
1200
1201
    /**
1202
     * add a key/value pair to the global option list
1203
     *
1204
     * @param string $name  The option name
1205
     * @param string $value The option value
1206
     */
1207 1
    public function addGlobalOption($name, $value)
1208
    {
1209 1
        $this->globalOptions[$name] = $value;
1210 1
    }
1211
1212
    /**
1213
     * remove an element form the global option list, identified by key
1214
     *
1215
     * @param  string $name The option name
1216
     */
1217 1
    public function removeGlobalOption($name)
1218
    {
1219 1
        if (isset($this->globalOptions[$name])) {
1220 1
            unset($this->globalOptions[$name]);
1221
        }
1222 1
    }
1223
1224
    /**
1225
     * get global command arguments list
1226
     *
1227
     * @return array Global command arguments list
1228
     */
1229 95
    public function getGlobalCommandArguments()
1230
    {
1231 95
        return $this->globalCommandArguments;
1232
    }
1233
1234
    /**
1235
     * add a value to the global command argument list
1236
     *
1237
     * @param string $value The command argument
1238
     */
1239 1
    public function addGlobalCommandArgument($value)
1240
    {
1241 1
        if (!in_array($value, $this->globalCommandArguments, true)) {
1242 1
            $this->globalCommandArguments[] = $value;
1243
        }
1244 1
    }
1245
1246
    /**
1247
     * remove an element form the global command argument list, identified by
1248
     * value
1249
     *
1250
     * @param  string $value The command argument
1251
     */
1252 1
    public function removeGlobalCommandArgument($value)
1253
    {
1254 1
        if (in_array($value, $this->globalCommandArguments, true)) {
1255 1
            $index = array_search($value, $this->globalCommandArguments);
1256 1
            unset($this->globalCommandArguments[$index]);
1257
        }
1258 1
    }
1259
1260
    /**
1261
     *  Save your local modifications to a new stash, and run git reset --hard to revert them.
1262
     *
1263
     * @param string|null $message
1264
     * @param boolean $includeUntracked
1265
     * @param boolean $keepIndex
1266
     */
1267 2
    public function stash($message = null, $includeUntracked = false, $keepIndex = false)
1268
    {
1269 2
        $stashCommand = StashCommand::getInstance($this);
1270 2
        $command = $stashCommand->save($message, $includeUntracked, $keepIndex);
1271 2
        $this->caller->execute($command);
1272 1
    }
1273
1274
    /**
1275
     * Shows stash list
1276
     *
1277
     * @param array|null $options
1278
     *
1279
     * @return array
1280
     */
1281 1
    public function stashList(array $options = null)
1282
    {
1283 1
        $stashCommand = StashCommand::getInstance($this);
1284 1
        $command = $stashCommand->listStashes($options);
1285 1
        $this->caller->execute($command);
1286 1
        return array_map('trim', $this->caller->getOutputLines(true));
1287
    }
1288
1289
    /**
1290
     * Shows details for a stash
1291
     *
1292
     * @param string $stash
1293
     *
1294
     * @return string
1295
     */
1296 1
    public function stashShow($stash)
1297
    {
1298 1
        $stashCommand = StashCommand::getInstance($this);
1299 1
        $command = $stashCommand->show($stash);
1300 1
        $this->caller->execute($command);
1301 1
        return $this->caller->getOutput();
1302
    }
1303
1304
    /**
1305
     * Drops a stash
1306
     *
1307
     * @param string $stash
1308
     */
1309 1
    public function stashDrop($stash)
1310
    {
1311 1
        $stashCommand = StashCommand::getInstance($this);
1312 1
        $command = $stashCommand->drop($stash);
1313 1
        $this->caller->execute($command);
1314 1
    }
1315
1316
    /**
1317
     * Applies a stash
1318
     *
1319
     * @param string $stash
1320
     * @param boolean $index
1321
     */
1322 1
    public function stashApply($stash, $index = false)
1323
    {
1324 1
        $stashCommand = StashCommand::getInstance($this);
1325 1
        $command = $stashCommand->apply($stash, $index);
1326 1
        $this->caller->execute($command);
1327 1
    }
1328
1329
    /**
1330
     *  Applies a stash, then removes it from the stash
1331
     *
1332
     * @param string $stash
1333
     * @param boolean $index
1334
     */
1335 1
    public function stashPop($stash, $index = false)
1336
    {
1337 1
        $stashCommand = StashCommand::getInstance($this);
1338 1
        $command = $stashCommand->pop($stash, $index);
1339 1
        $this->caller->execute($command);
1340 1
    }
1341
1342
    /**
1343
     *  Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created
1344
     *
1345
     * @param string $branch
1346
     * @param string $stash
1347
     */
1348 1
    public function stashBranch($branch, $stash)
1349
    {
1350 1
        $stashCommand = StashCommand::getInstance($this);
1351 1
        $command = $stashCommand->branch($branch, $stash);
1352 1
        $this->caller->execute($command);
1353 1
    }
1354
1355
    /**
1356
     *  Save your local modifications to a new stash, and run git reset --hard to revert them.
1357
     *
1358
     */
1359
    public function stashClear()
1360
    {
1361
        $stashCommand = StashCommand::getInstance($this);
1362
        $command = $stashCommand->clear();
1363
        $this->caller->execute($command);
1364
    }
1365
1366
    /**
1367
     *  Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the
1368
     *  ref namespace.
1369
     *
1370
     * @return string
1371
     */
1372 1
    public function stashCreate()
1373
    {
1374 1
        $stashCommand = StashCommand::getInstance($this);
1375 1
        $command = $stashCommand->clear();
1376 1
        $this->caller->execute($command);
1377 1
        return $this->caller->getOutput();
1378
    }
1379
}
1380