Passed
Push — master ( 40f748...13091e )
by Andreas
12:20
created

midcom_services_rcs_backend_git::update()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 15
ccs 10
cts 10
cp 1
crap 2
rs 9.9666
1
<?php
2
/**
3
 * @package midcom.services.rcs
4
 * @author CONTENT CONTROL https://contentcontrol.berlin
5
 * @copyright CONTENT CONTROL https://contentcontrol.berlin
6
 */
7
8
/**
9
 * @package midcom.services.rcs
10
 */
11
class midcom_services_rcs_backend_git extends midcom_services_rcs_backend
12
{
13
    /**
14
     * Save a new revision
15
     */
16 1
    public function update(string $user_id, string $updatemessage = '')
17
    {
18 1
        $filename = $this->generate_filename();
19 1
        $relative_path = $this->relative_path($filename);
20 1
        $author = $user_id . ' <' . $user_id . '@' . $_SERVER['REMOTE_ADDR'] . '>';
21 1
        $command = 'commit -q --allow-empty --allow-empty-message -m ' . escapeshellarg($updatemessage) .
22 1
            ' --author ' . escapeshellarg($author) . ' ' . $relative_path;
23
24 1
        $this->write_object($filename);
25
        // avoid the separate add cmd where possible to mitigate concurrency issues
26 1
        if (!$this->read_handle('ls-files ' . $filename)[0]) {
27 1
            $this->exec('add ' . $relative_path);
28
        }
29
30 1
        $this->exec($command);
31
    }
32
33
    /**
34
     * Get a revision
35
     */
36
    public function get_revision(string $revision) : array
37
    {
38
        $filename = $this->generate_filename();
39
        $lines = $this->read_handle('show ' . $revision . ':' . $this->relative_path($filename));
40
        $mapper = new midcom_helper_exporter_xml();
41
        return $mapper->data2array(implode("\n", $lines));
42
    }
43
44 1
    protected function load_history() : array
45
    {
46 1
        $filename = $this->generate_filename();
47 1
        if (!is_readable($filename)) {
48 1
            debug_add('file ' . $filename . ' is not readable, returning empty result', MIDCOM_LOG_INFO);
49 1
            return [];
50
        }
51
52 1
        $lines = $this->read_handle('log --shortstat --format=format:"%h%n%ae%n%at%n%s" ' . $this->relative_path($filename));
53 1
        $total = count($lines);
54 1
        $revisions = [];
55
56 1
        for ($i = 0; $i < $total; $i += 6) {
57 1
            [$user, $ip] = explode('@', $lines[$i + 1], 2);
58 1
            $stat = preg_replace('/.*?\d file changed/', '', $lines[$i + 4]);
59 1
            $stat = preg_replace('/, (\d+) .+?tions?\(([\+\-])\)/', '$2$1 ', $stat);
60
61 1
            $revisions[$lines[$i]] = [
62 1
                'revision' => $lines[$i],
63 1
                'date' => $lines[$i + 2],
64 1
                'lines' => $stat,
65 1
                'user' => $user,
66 1
                'ip' => $ip,
67 1
                'message' => $lines[$i + 3]
68 1
            ];
69
        }
70
71 1
        return $revisions;
72
    }
73
74 1
    protected function generate_filename() : string
75
    {
76 1
        $root = $this->config->get_rootdir();
77 1
        $initialized = true;
78 1
        if (!file_exists($root . '/.git')) {
79 1
            if ((count(scandir($root)) > 2)) {
80
                // This is probably an old rcs dir
81
                throw new midcom_error($root . ' is not empty. Run tools/rcs2git to convert');
82
            }
83 1
            $initialized = false;
84
        }
85 1
        $filename = parent::generate_filename();
86
87 1
        if (!$initialized) {
88 1
            $this->exec('init');
89 1
            $this->exec('config user.email "midcom.rcs@localhost"');
90 1
            $this->exec('config user.name "midcom.rcs"');
91
        }
92 1
        return $filename;
93
    }
94
95 1
    protected function read_handle(string $command) : array
96
    {
97 1
        return parent::read_handle($this->get_command_prefix() . ' ' . $command);
98
    }
99
100 1
    private function exec(string $command)
101
    {
102 1
        $this->run_command($this->get_command_prefix($command != 'init') . ' ' . $command);
103
    }
104
105 1
    private function get_command_prefix(bool $initialized = true) : string
106
    {
107 1
        $prefix = 'git -C ' . $this->config->get_rootdir();
108 1
        if ($initialized) {
109
            // These help for the nested repo case
110 1
            $prefix .= ' --git-dir=' . $this->config->get_rootdir() . '/.git ' . ' --work-tree=' . $this->config->get_rootdir();
111
        }
112 1
        return $prefix;
113
    }
114
115 1
    private function relative_path(string $filename) : string
116
    {
117 1
        $relative_path = substr($filename, strlen($this->config->get_rootdir()));
118 1
        return escapeshellarg(trim($relative_path, '/'));
119
    }
120
}
121