GitStatusCommand::stageFiles()   C
last analyzed

Complexity

Conditions 17
Paths 32

Size

Total Lines 66
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 31
c 1
b 0
f 0
nc 32
nop 1
dl 0
loc 66
rs 5.2166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
// src/VersionControl/GitCommandBundle/GitCommands/Command/GitSyncCommand.php
3
4
/*
5
 * This file is part of the GitCommandBundle package.
6
 *
7
 * (c) Paul Schweppe <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace VersionControl\GitCommandBundle\GitCommands\Command;
14
15
use RuntimeException;
16
use VersionControl\GitCommandBundle\Entity\GitFile;
17
use VersionControl\GitCommandBundle\GitCommands\Exception\RunGitCommandException;
18
19
/**
20
 * @author Paul Schweppe <[email protected]>
21
 */
22
class GitStatusCommand extends AbstractGitCommand
23
{
24
    /**
25
     * @var string Git Status Hash.
26
     *             Used to make sure no changes has occurred since last check
27
     */
28
    protected $statusHash;
29
30
    /**
31
     * Get hash of git status.
32
     *
33
     * @return string hash
34
     * @throws RuntimeException
35
     * @throws RunGitCommandException
36
     */
37
    public function getStatusHash(): string
38
    {
39
        if (!$this->statusHash) {
40
            $statusData = $this->getStatus();
41
            $this->statusHash = hash('md5', $statusData);
42
        }
43
44
        return $this->statusHash;
45
    }
46
47
    /**
48
     * Gets all files that need to be commited.
49
     *
50
     * @return array Array of GitFile objects
51
     * @throws RuntimeException
52
     * @throws RunGitCommandException
53
     */
54
    public function getFilesToCommit(): array
55
    {
56
        $stausData = $this->getStatus();
57
        $this->statusHash = hash('md5', $stausData);
58
59
        return $this->processStatus($stausData);
60
    }
61
62
    /**
63
     * Git status command
64
     * Response:
65
     *  D feedback.html
66
     *  ?? time-selectors/work.html.
67
     *
68
     * @return string Command Response
69
     * @throws RunGitCommandException
70
     * @throws RuntimeException
71
     */
72
    public function getStatus(): string
73
    {
74
        return $this->command->runCommand('git status -u --porcelain', true, false);
75
    }
76
77
    /**
78
     * Stage files for commit.
79
     * In the short-format, the status of each path is shown as
80
     * XY PATH1 -> PATH2
81
     * where PATH1 is the path in the HEAD, and the ` -> PATH2` part is shown only when PATH1 corresponds to a
82
     * different path in the index/worktree (i.e. the file is renamed). The XY is a two-letter status code.
83
     *
84
     * The fields (including the ->) are separated from each other by a single space. If a filename contains whitespace
85
     * or other nonprintable characters, that field will be quoted in the manner of a C string literal: surrounded by
86
     * ASCII double quote (34) characters, and with interior special characters backslash-escaped.
87
     * For paths with merge conflicts, X and Y show the modification states of each side of the merge. For paths that
88
     * do not have merge conflicts, X shows the status of the index, and Y shows the status of the work tree. For
89
     * untracked paths, XY are ??. Other status codes can be interpreted as follows:
90
     * ' ' = unmodified
91
     * M = modified
92
     * A = added
93
     * D = deleted
94
     * R = renamed
95
     * C = copied
96
     * U = updated but unmerged
97
     *
98
     * Ignored files are not listed, unless --ignored option is in effect, in which case XY are !!.
99
     *
100
     * X          Y     Meaning
101
     * -------------------------------------------------
102
     * [MD]   not updated
103
     * M        [ MD]   updated in index
104
     * A        [ MD]   added to index
105
     * D         [ M]   deleted from index
106
     * R        [ MD]   renamed in index
107
     * C        [ MD]   copied in index
108
     * [MARC]           index and work tree matches
109
     * [ MARC]     M    work tree changed since index
110
     * [ MARC]     D    deleted in work tree
111
     * -------------------------------------------------
112
     * D           D    unmerged, both deleted
113
     * A           U    unmerged, added by us
114
     * U           D    unmerged, deleted by them
115
     * U           A    unmerged, added by them
116
     * D           U    unmerged, deleted by us
117
     * A           A    unmerged, both added
118
     * U           U    unmerged, both modified
119
     * -------------------------------------------------
120
     * ?           ?    untracked
121
     * !           !    ignored
122
     * -------------------------------------------------
123
     * If -b is used the short-format status is preceded by a line
124
     *
125
     * @TODO: No Support for copy yet
126
     *
127
     * @param array $files
128
     *
129
     * @throws RunGitCommandException
130
     * @throws RuntimeException
131
     */
132
    public function stageFiles(array $files): void
133
    {
134
        $gitFiles = $this->getFilesToCommit();
135
136
        //Validated that this status is same as previous
137
        $deleteFiles = array();
138
        $addFiles = array();
139
140
        $flippedFiles = array_flip($files);
141
142
        foreach ($gitFiles as $fileEntity) {
143
            if (!isset($flippedFiles[$fileEntity->getPath1()])) {
144
                continue;
145
            }
146
147
            if ($fileEntity->getWorkTreeStatus() === '!') {
148
                //do Nothing ignore
149
                continue;
150
            }
151
152
            if ($fileEntity->getWorkTreeStatus() === 'D'
153
                && ($fileEntity->getIndexStatus() === ' '
154
                    || $fileEntity->getIndexStatus() === 'M'
155
                    || $fileEntity->getIndexStatus() === 'A'
156
                )
157
            ) {
158
                //Delete files
159
                //[ MA]     D    deleted in work tree
160
                $deleteFiles[] = escapeshellarg($fileEntity->getPath1());
161
                continue;
162
            }
163
164
            if ($fileEntity->getIndexStatus() === 'R' && ($fileEntity->getWorkTreeStatus() === 'D')) {
165
                //Rename delete
166
                //[R]     D    deleted in work tree
167
                $deleteFiles[] = escapeshellarg($fileEntity->getPath2());
168
                continue;
169
            }
170
171
            if ($fileEntity->getIndexStatus() === 'R'
172
                && ($fileEntity->getWorkTreeStatus() === 'M'
173
                    || $fileEntity->getWorkTreeStatus() === 'A'
174
                    || $fileEntity->getWorkTreeStatus() === ' ')
175
            ) {
176
                //Rename ADD
177
                //[R]     [ M]
178
                $addFiles[] = escapeshellarg($fileEntity->getPath2());
179
                continue;
180
            }
181
182
            if ($fileEntity->getWorkTreeStatus() === ' ') {
183
                //[MARC]           index and work tree matches
184
                //Do Nothing
185
                continue;
186
            }
187
188
            $addFiles[] = escapeshellarg($fileEntity->getPath1());
189
        }
190
191
        //Run the commands once for add and delete
192
        if (count($deleteFiles) > 0) {
193
            $this->command->runCommand('git rm ' . implode(' ', $deleteFiles));
194
        }
195
196
        if (count($addFiles) > 0) {
197
            $this->command->runCommand('git add ' . implode(' ', $addFiles));
198
        }
199
    }
200
201
    /**
202
     * Stages the file to be committed.
203
     * Currently supports adding and removing file.
204
     *
205
     * @TODO Make it more effecient
206
     *
207
     * @param string $file path to file to commit
208
     *
209
     * @throws RuntimeException
210
     * @throws RunGitCommandException
211
     */
212
    public function stageFile($file): void
213
    {
214
        $this->stageFiles(array($file));
215
    }
216
217
    /**
218
     * Process the git status data into GitFile objects.
219
     *
220
     * @param string $statusData
221
     *
222
     * @return array Array of GitFile objects
223
     */
224
    protected function processStatus($statusData): array
225
    {
226
        $lines = $this->splitOnNewLine($statusData, false);
227
228
        if (!is_array($lines) || count($lines) === 0) {
0 ignored issues
show
introduced by
The condition is_array($lines) is always true.
Loading history...
229
            return [];
230
        }
231
232
        $files = array();
233
        foreach ($lines as $line) {
234
            if (trim($line)) {
235
                $files[] = new GitFile($line, $this->command->getGitPath());
236
            }
237
        }
238
239
        return $files;
240
    }
241
}
242