GitRepo.configured_options()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
c 2
b 0
f 0
dl 0
loc 5
rs 9.4285
1
import sys
2
from subprocess import STDOUT
3
from subprocess import CalledProcessError
4
from subprocess import call
5
from subprocess import check_output
6
7
if sys.version_info[0] > 2:
8
    NOT_A_GIT_REPO_STDERR = b'Not a git repository'
9
else:
10
    NOT_A_GIT_REPO_STDERR = 'Not a git repository'
11
12
13
class GitRepoNotFound(CalledProcessError):
14
    """Raised when trying to instantiate a GitRepo object on a file path that is not a git repo."""
15
    pass
16
17
18
class GitCommandException(CalledProcessError):
19
    """Raised for any other git command failures."""
20
    pass
21
22
23
# def is_git_url(url=None):
24
#     return bool(re.match(r'((git|ssh|http(s)?)|(git@[\w\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)(/)?'), url)
25
26
27
class GitRepo(object):
28
    def __init__(self, path=None):
29
        self._path = path
30
        self.base_command = ['git']
31
        if path is not None:
32
            self.base_command += ['-C', path]
33
        try:
34
            # Ensure that either the current working directory, or the specified path, is part of a git repository.
35
            self._check_output(['rev-parse'])
36
        except GitCommandException as command_error:
37
            if NOT_A_GIT_REPO_STDERR in command_error.output:
38
                raise GitRepoNotFound(*command_error.args)
39
40
    def _check_output(self, command):
41
        """Wrap the call to subprocess.check_output() to raise customized exceptions and always return strings."""
42
        try:
43
            if sys.version_info[0] > 2:
44
                return check_output(self.base_command + command, stderr=STDOUT).decode('utf8')
45
            else:
46
                return check_output(self.base_command + command, stderr=STDOUT)
47
        except CalledProcessError as command_error:
48
            raise GitCommandException(*command_error.args)
49
50
    def _call(self, command):
51
        return call(self.base_command + command)
52
53
    @property
54
    def top_level_directory_path(self):
55
        """What is the top level directory of the git repo."""
56
        return self._check_output(['rev-parse', '--show-toplevel'])
57
58
    @property
59
    def is_dirty(self):
60
        return bool(self._call(['diff', '--cached', '--quiet']))
61
62
    @property
63
    def branch(self):
64
        return self._check_output(['branch'])[2:-1]
65
66
    @property
67
    def configured_options(self):
68
        """What are the configured options in the git repo."""
69
        stdout_lines = self._check_output(['config', '--list']).splitlines()
70
        return {key: value for key, value in [line.split('=') for line in stdout_lines]}
71
72
    @property
73
    def remotes(self):
74
        return list(set(
75
            [conf_var[1] for conf_var in [key.split('.') for key in self.configured_options.keys()]
76
                if conf_var[0] == 'remote']
77
        ))
78
79
    @property
80
    def remote_urls(self):
81
        return {remote: self.configured_options.get('remote.{}.url'.format(remote)) for remote in self.remotes}
82