Commit   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 66
c 4
b 0
f 0
dl 0
loc 189
rs 9.52
wmc 36

14 Methods

Rating   Name   Duplication   Size   Complexity  
C getData() 0 43 14
A getShortHash() 0 3 1
A getTree() 0 3 1
B getReferences() 0 25 7
A getMessage() 0 8 2
A reset() 0 4 2
A fromRaw() 0 6 1
A getRaw() 0 3 1
A getParentHashes() 0 3 1
A getTreeHash() 0 3 1
A getCommitter() 0 3 1
A getParents() 0 9 2
A getAuthor() 0 3 1
A getSignature() 0 3 1
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of Biurad opensource projects.
5
 *
6
 * @copyright 2022 Biurad Group (https://biurad.com/)
7
 * @license   https://opensource.org/licenses/BSD-3-Clause License
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 Biurad\Git;
14
15
/**
16
 * Represents a git commit.
17
 *
18
 * @author Divine Niiquaye Ibok <[email protected]>
19
 */
20
class Commit extends GitObject
21
{
22
    /** @var array<string,mixed> */
23
    private array $data = [];
24
25
    private ?string $content = null;
26
27
    public static function fromRaw(string $rawString, Repository $repo, string $hash): self
28
    {
29
        $commit = new self($repo, $hash);
30
        $commit->content = $rawString;
31
32
        return $commit;
33
    }
34
35
    /**
36
     * @param bool $soft If false, repository will be reset to this commit
37
     * @param bool $keep Preserve uncommitted local changes if true
38
     */
39
    public function reset(bool $soft = true, bool $keep = false): void
40
    {
41
        $this->data = [];
42
        $this->repository->reset($soft ? null : $this->__toString(), $keep);
43
    }
44
45
    /**
46
     * Returns the commit as string.
47
     */
48
    public function getRaw(): string
49
    {
50
        return $this->content ??= $this->getData('content');
51
    }
52
53
    /**
54
     * Returns the short hash of a commit.
55
     */
56
    public function getShortHash(): string
57
    {
58
        return $this->data['shortHash'] ??= \substr((string) $this, 0, 7);
59
    }
60
61
    /**
62
     * @return array<int,string> An array of parent SHA1 hashes
63
     */
64
    public function getParentHashes(): array
65
    {
66
        return $this->getData('parent') ?? [];
67
    }
68
69
    /**
70
     * @return array<int,self> An array of parent commits
71
     */
72
    public function getParents(): array
73
    {
74
        $commits = [];
75
76
        foreach ($this->getParentHashes() as $parentHash) {
77
            $commits[] = $this->repository->getCommit($parentHash);
78
        }
79
80
        return $commits;
81
    }
82
83
    /**
84
     * Returns the tree hash of the commit.
85
     */
86
    public function getTreeHash(): string
87
    {
88
        return $this->getData('treeHash');
89
    }
90
91
    /**
92
     * Returns the tree object of the commit.
93
     */
94
    public function getTree(): Commit\Tree
95
    {
96
        return $this->data['tree'] ??= new Commit\Tree($this->repository, $this->getTreeHash());
97
    }
98
99
    /**
100
     * Returns the author of the commit.
101
     */
102
    public function getAuthor(): Commit\Identity
103
    {
104
        return $this->getData('author');
105
    }
106
107
    /**
108
     * Returns the committer of the commit.
109
     */
110
    public function getCommitter(): Commit\Identity
111
    {
112
        return $this->getData('committer');
113
    }
114
115
    /**
116
     * Returns the commit message.
117
     */
118
    public function getMessage(): Commit\Message
119
    {
120
        if (!isset($this->data['msg-object'])) {
121
            $data = \explode("\n\n", $this->getData('message'), 2);
122
            $this->data['msg-object'] = new Commit\Message($data[0], $data[1] ?? null);
123
        }
124
125
        return $this->data['msg-object'];
126
    }
127
128
    /**
129
     * @return null|string get the verified signature of a commit
130
     */
131
    public function getSignature(): ?string
132
    {
133
        return $this->getData('gsgSign');
134
    }
135
136
    /**
137
     * @return array<int,Revision> An array of (Branch, Tag, Squash)
138
     */
139
    public function getReferences(): array
140
    {
141
        $o = $this->repository->run('for-each-ref', ['--contains', $this->__toString(), '--format=%(refname) %(objectname)']);
142
143
        if (empty($o) || 0 !== $this->repository->getExitCode()) {
144
            throw new \RuntimeException(\sprintf('Failed to get commit references for "%s"', $this->__toString()));
145
        }
146
147
        if (!isset($this->data[$i = \md5($o)])) {
148
            foreach (\explode("\n", $o) as $line) {
149
                if (empty($line)) {
150
                    continue;
151
                }
152
                [$ref, $hash] = \explode(' ', $line, 2);
153
154
                if (\str_starts_with($ref, 'refs/tags/')) {
155
                    $this->data[$i][] = new Tag($this->repository, $ref, $hash);
156
                    continue;
157
                }
158
159
                $this->data[$i][] = new Branch($this->repository, $ref, $hash);
160
            }
161
        }
162
163
        return $this->data[$i];
164
    }
165
166
    private function getData(string $name): mixed
167
    {
168
        if (!isset($this->data[$name])) {
169
            $o = $this->content ?? $this->repository->run('cat-file', ['commit', $this->__toString()]);
170
171
            if (empty($o) || 0 !== $this->repository->getExitCode()) {
172
                throw new \RuntimeException(\sprintf('Failed to get commit data for "%s"', $this->__toString()));
173
            }
174
175
            foreach (\explode("\n", $this->data['content'] = $o) as $line) {
176
                if (isset($this->data['gpgSign'])) {
177
                    if (\str_ends_with($line, '-----END PGP SIGNATURE-----')) {
178
                        $pos = \strpos($o, "-----\n \n\n");
179
                        $this->data['message'] = \substr($o, $pos ? $pos + 9 : \strpos($o, "-----\n\n") + 7);
180
                        break;
181
                    }
182
183
                    if (!empty($line)) {
184
                        $this->data['gpgSign'] .= $line."\n";
185
                    }
186
                    continue;
187
                } elseif (empty($line)) {
188
                    continue;
189
                }
190
                [$key, $value] = \explode(' ', $line, 2);
191
192
                if ('tree' === $key) {
193
                    $this->data['treeHash'] = $value;
194
                } elseif ('parent' === $key) {
195
                    $this->data['parent'][] = $value;
196
                } elseif (\in_array($key, ['author', 'committer'], true)) {
197
                    \preg_match('/(([^\n]*) <([^\n]*)> (\d+ [+-]\d{4}))/A', $value, $author);
198
                    $this->data[$key] = new Commit\Identity($author[2], $author[3], \DateTime::createFromFormat('U e O', $author[4].' UTC'));
199
                } elseif ('gpgsig' === $key) {
200
                    $this->data['gpgSign'] = '';
201
                } else {
202
                    $this->data['message'] = \substr($o, \strpos($o, "\n\n") + 2);
203
                    break;
204
                }
205
            }
206
        }
207
208
        return $this->data[$name] ?? null;
209
    }
210
}
211