Completed
Push — develop ( 05bbe8...fc5dde )
by
unknown
11:18 queued 09:33
created

Repository::createFromRemote()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3.0052

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 11
cts 12
cp 0.9167
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 12
nc 4
nop 4
crap 3.0052
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
     *
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|NodeObject $path the path to store
199
     *
200
     * @throws \RuntimeException
201
     * @throws \Symfony\Component\Process\Exception\LogicException
202
     * @throws InvalidArgumentException
203
     * @throws \Symfony\Component\Process\Exception\RuntimeException
204
     * @return Repository
205
     */
206 90
    public function stage($path = '.')
207
    {
208 90
        $this->caller->execute(MainCommand::getInstance($this)->add($path));
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 206 can also be of type object<GitElephant\Objects\NodeObject>; however, GitElephant\Command\MainCommand::add() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
209
210 90
        return $this;
211
    }
212
213
    /**
214
     * Unstage a tree content
215
     *
216
     * @param string|NodeObject $path the path to unstage
217
     *
218
     * @throws \RuntimeException
219
     * @throws \Symfony\Component\Process\Exception\LogicException
220
     * @throws InvalidArgumentException
221
     * @throws \Symfony\Component\Process\Exception\RuntimeException
222
     * @return Repository
223
     */
224 2
    public function unstage($path)
225
    {
226 2
        $this->caller->execute(MainCommand::getInstance($this)->unstage($path), true, null, 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 2
        return $this;
229
    }
230
231
    /**
232
     * Move a file/directory
233
     *
234
     * @param string|NodeObject $from source path
235
     * @param string|NodeObject $to   destination path
236
     *
237
     * @throws \RuntimeException
238
     * @throws \Symfony\Component\Process\Exception\LogicException
239
     * @throws \InvalidArgumentException
240
     * @throws InvalidArgumentException
241
     * @throws \Symfony\Component\Process\Exception\RuntimeException
242
     * @return Repository
243
     */
244 1
    public function move($from, $to)
245
    {
246 1
        $this->caller->execute(MainCommand::getInstance($this)->move($from, $to));
247
248 1
        return $this;
249
    }
250
251
    /**
252
     * Remove a file/directory
253
     *
254
     * @param string|NodeObject $path      the path to remove
255
     * @param bool              $recursive recurse
256
     * @param bool              $force     force
257
     *
258
     * @throws \RuntimeException
259
     * @throws \Symfony\Component\Process\Exception\LogicException
260
     * @throws \InvalidArgumentException
261
     * @throws InvalidArgumentException
262
     * @throws \Symfony\Component\Process\Exception\RuntimeException
263
     * @return Repository
264
     */
265 1
    public function remove($path, $recursive = false, $force = false)
266
    {
267 1
        $this->caller->execute(MainCommand::getInstance($this)->remove($path, $recursive, $force));
268
269 1
        return $this;
270
    }
271
272
    /**
273
     * Commit content to the repository, eventually staging all unstaged content
274
     *
275
     * @param string        $message    the commit message
276
     * @param bool          $stageAll   whether to stage on not everything before commit
277
     * @param string|null   $ref        the reference to commit to (checkout -> commit -> checkout previous)
278
     * @param string|Author $author     override the author for this commit
279
     * @param bool          $allowEmpty override the author for this commit
280
     *
281
     * @throws \RuntimeException
282
     * @throws \InvalidArgumentException
283
     * @throws \Symfony\Component\Process\Exception\RuntimeException
284
     * @return Repository
285
     */
286 85
    public function commit($message, $stageAll = false, $ref = null, $author = null, $allowEmpty = false)
287
    {
288 85
        $currentBranch = null;
289 85
        if (! is_null($ref)) {
290 1
            $currentBranch = $this->getMainBranch();
291 1
            $this->checkout($ref);
292
        }
293 85
        if ($stageAll) {
294 83
            $this->stage();
295
        }
296 85
        $this->caller->execute(MainCommand::getInstance($this)->commit($message, $stageAll, $author, $allowEmpty));
297 85
        if (! is_null($ref)) {
298 1
            $this->checkout($currentBranch);
0 ignored issues
show
Bug introduced by
It seems like $currentBranch defined by null on line 288 can be null; however, GitElephant\Repository::checkout() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
299
        }
300
301 85
        return $this;
302
    }
303
304
    /**
305
     * rev-parse command - often used to return a commit tag.
306
     *
307
     * @param array                    $options the options to apply to rev-parse
308
     * @param string|NodeObject|Commit $arg     the argument (may be a branch head, etc)
309
     *
310
     * @throws \RuntimeException
311
     * @throws \InvalidArgumentException
312
     * @throws \Symfony\Component\Process\Exception\RuntimeException
313
     * @return array
314
     */
315 1
    public function revParse($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\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 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 6
                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 5
            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|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
     * @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 $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
     * @throws InvalidArgumentException
860
     * @throws \Symfony\Component\Process\Exception\RuntimeException
861
     * @return \GitElephant\Objects\Log
862
     */
863 3
    public function getObjectLog(NodeObject $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|NodeObject       $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<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
     * @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\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
     * @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
     * @param string $args
1047
     * @throws \RuntimeException
1048
     * @throws \Symfony\Component\Process\Exception\LogicException
1049
     * @throws InvalidArgumentException
1050
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1051
     */
1052 1
    public function push($to = null, $ref = null, $args = null)
1053
    {
1054 1
        $this->caller->execute(PushCommand::getInstance($this)->push($to, $ref, $args));
1055 1
    }
1056
1057
    /**
1058
     * get the humanish name of the repository
1059
     *
1060
     * @return string
1061
     */
1062 2
    public function getHumanishName()
1063
    {
1064 2
        $name = substr($this->getPath(), strrpos($this->getPath(), '/') + 1);
1065 2
        $name = str_replace('.git', '.', $name);
1066 2
        $name = str_replace('.bundle', '.', $name);
1067
1068 2
        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
     * @throws \Symfony\Component\Process\Exception\LogicException
1079
     * @throws InvalidArgumentException
1080
     * @throws \Symfony\Component\Process\Exception\RuntimeException
1081
     * @return array
1082
     */
1083 1
    public function outputContent(NodeObject $obj, $treeish)
1084
    {
1085 1
        $command = CatFileCommand::getInstance($this)->content($obj, $treeish);
1086
1087 1
        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
    /**
1110
     * Get the path
1111
     *
1112
     * @return string
1113
     */
1114 63
    public function getPath()
1115
    {
1116 63
        return $this->path;
1117
    }
1118
1119
    /**
1120
     * Get the repository name
1121
     *
1122
     * @return string
1123
     */
1124 1
    public function getName()
1125
    {
1126 1
        return $this->name;
1127
    }
1128
1129
    /**
1130
     * Set the repository name
1131
     *
1132
     * @param string $name the repository name
1133
     */
1134 1
    public function setName($name)
1135
    {
1136 1
        $this->name = $name;
1137 1
    }
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
    /**
1150
     * Caller getter
1151
     *
1152
     * @return \GitElephant\Command\Caller\Caller
1153
     */
1154 89
    public function getCaller()
1155
    {
1156 89
        return $this->caller;
1157
    }
1158
1159
    /**
1160
     * get global config list
1161
     *
1162
     * @return array Global config list
1163
     */
1164 95
    public function getGlobalConfigs()
1165
    {
1166 95
        return $this->globalConfigs;
1167
    }
1168
1169
    /**
1170
     * add a key/value pair to the global config list
1171
     *
1172
     * @param string $name  The config name
1173
     * @param string $value The config value
1174
     */
1175 1
    public function addGlobalConfig($name, $value)
1176
    {
1177 1
        $this->globalConfigs[$name] = $value;
1178 1
    }
1179
1180
    /**
1181
     * remove an element form the global config list, identified by key
1182
     *
1183
     * @param  string $name The config name
1184
     */
1185 1
    public function removeGlobalConfig($name)
1186
    {
1187 1
        if (isset($this->globalConfigs[$name])) {
1188 1
            unset($this->globalConfigs[$name]);
1189
        }
1190 1
    }
1191
1192
    /**
1193
     * get global options list
1194
     *
1195
     * @return array Global options list
1196
     */
1197 95
    public function getGlobalOptions()
1198
    {
1199 95
        return $this->globalOptions;
1200
    }
1201
1202
    /**
1203
     * add a key/value pair to the global option list
1204
     *
1205
     * @param string $name  The option name
1206
     * @param string $value The option value
1207
     */
1208 1
    public function addGlobalOption($name, $value)
1209
    {
1210 1
        $this->globalOptions[$name] = $value;
1211 1
    }
1212
1213
    /**
1214
     * remove an element form the global option list, identified by key
1215
     *
1216
     * @param  string $name The option name
1217
     */
1218 1
    public function removeGlobalOption($name)
1219
    {
1220 1
        if (isset($this->globalOptions[$name])) {
1221 1
            unset($this->globalOptions[$name]);
1222
        }
1223 1
    }
1224
1225
    /**
1226
     * get global command arguments list
1227
     *
1228
     * @return array Global command arguments list
1229
     */
1230 95
    public function getGlobalCommandArguments()
1231
    {
1232 95
        return $this->globalCommandArguments;
1233
    }
1234
1235
    /**
1236
     * add a value to the global command argument list
1237
     *
1238
     * @param string $value The command argument
1239
     */
1240 1
    public function addGlobalCommandArgument($value)
1241
    {
1242 1
        if (!in_array($value, $this->globalCommandArguments, true)) {
1243 1
            $this->globalCommandArguments[] = $value;
1244
        }
1245 1
    }
1246
1247
    /**
1248
     * remove an element form the global command argument list, identified by
1249
     * value
1250
     *
1251
     * @param  string $value The command argument
1252
     */
1253 1
    public function removeGlobalCommandArgument($value)
1254
    {
1255 1
        if (in_array($value, $this->globalCommandArguments, true)) {
1256 1
            $index = array_search($value, $this->globalCommandArguments);
1257 1
            unset($this->globalCommandArguments[$index]);
1258
        }
1259 1
    }
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 2
    public function stash($message = null, $includeUntracked = false, $keepIndex = false)
1269
    {
1270 2
        $stashCommand = StashCommand::getInstance($this);
1271 2
        $command = $stashCommand->save($message, $includeUntracked, $keepIndex);
1272 2
        $this->caller->execute($command);
1273 1
    }
1274
1275
    /**
1276
     * Shows stash list
1277
     *
1278
     * @param array|null $options
1279
     *
1280
     * @return array
1281
     */
1282 1
    public function stashList(array $options = null)
1283
    {
1284 1
        $stashCommand = StashCommand::getInstance($this);
1285 1
        $command = $stashCommand->listStashes($options);
1286 1
        $this->caller->execute($command);
1287 1
        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 1
    public function stashShow($stash)
1298
    {
1299 1
        $stashCommand = StashCommand::getInstance($this);
1300 1
        $command = $stashCommand->show($stash);
1301 1
        $this->caller->execute($command);
1302 1
        return $this->caller->getOutput();
1303
    }
1304
1305
    /**
1306
     * Drops a stash
1307
     *
1308
     * @param string $stash
1309
     */
1310 1
    public function stashDrop($stash)
1311
    {
1312 1
        $stashCommand = StashCommand::getInstance($this);
1313 1
        $command = $stashCommand->drop($stash);
1314 1
        $this->caller->execute($command);
1315 1
    }
1316
1317
    /**
1318
     * Applies a stash
1319
     *
1320
     * @param string $stash
1321
     * @param boolean $index
1322
     */
1323 1
    public function stashApply($stash, $index = false)
1324
    {
1325 1
        $stashCommand = StashCommand::getInstance($this);
1326 1
        $command = $stashCommand->apply($stash, $index);
1327 1
        $this->caller->execute($command);
1328 1
    }
1329
1330
    /**
1331
     *  Applies a stash, then removes it from the stash
1332
     *
1333
     * @param string $stash
1334
     * @param boolean $index
1335
     */
1336 1
    public function stashPop($stash, $index = false)
1337
    {
1338 1
        $stashCommand = StashCommand::getInstance($this);
1339 1
        $command = $stashCommand->pop($stash, $index);
1340 1
        $this->caller->execute($command);
1341 1
    }
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 1
    public function stashBranch($branch, $stash)
1350
    {
1351 1
        $stashCommand = StashCommand::getInstance($this);
1352 1
        $command = $stashCommand->branch($branch, $stash);
1353 1
        $this->caller->execute($command);
1354 1
    }
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 1
    public function stashCreate()
1374
    {
1375 1
        $stashCommand = StashCommand::getInstance($this);
1376 1
        $command = $stashCommand->clear();
1377 1
        $this->caller->execute($command);
1378 1
        return $this->caller->getOutput();
1379
    }
1380
}
1381