Passed
Pull Request — develop (#167)
by
unknown
02:27
created

is_sha()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 1
rs 9.4285
c 1
b 0
f 0
1
"""Utilities to call Git commands."""
2
3 1
import os
4 1
import logging
5 1
from contextlib import suppress
6
import re
7 1
8 1
from . import common, settings
9 1
from .shell import call
10
from .exceptions import ShellError
11
12 1
13
log = logging.getLogger(__name__)
14
15 1
16 1
def git(*args, **kwargs):
17
    return call('git', *args, **kwargs)
18
19 1
20
def clone(repo, path, *, cache=settings.CACHE, sparse_paths=None, rev=None):
21 1
    """Clone a new Git repository."""
22
    log.debug("Creating a new repository...")
23 1
24 1
    name = repo.split('/')[-1]
25 1
    if name.endswith(".git"):
26
        name = name[:-4]
27 1
28 1
    reference = os.path.join(cache, name + ".reference")
29 1
    if not os.path.isdir(reference):
30
        git('clone', '--mirror', repo, reference)
31 1
32
    normpath = os.path.normpath(path)
33
    if sparse_paths:
34 1
        os.mkdir(normpath)
35
        git('-C', normpath, 'init')
36 1
        git('-C', normpath, 'config', 'core.sparseCheckout', 'true')
37 1
        git('-C', normpath, 'remote', 'add', '-f', 'origin', reference)
38 1
39 1
        with open("%s/%s/.git/info/sparse-checkout" % (os.getcwd(), normpath), 'w') as fd:
40 1
            fd.writelines(sparse_paths)
41 1
        with open("%s/%s/.git/objects/info/alternates" % (os.getcwd(), normpath), 'w') as fd:
42 1
            fd.write("%s/objects" % reference)
43
44 1
        # We use directly the revision requested here in order to respect,
45 1
        # that not all repos have `master` as their default branch
46
        git('-C', normpath, 'pull', 'origin', rev)
47
    else:
48 1
        git('clone', '--reference', reference, repo, os.path.normpath(path))
49
50 1
51
def is_sha(rev):
52 1
    """Heuristically determine whether a revision corresponds to a commit SHA.
53 1
54 1
    Any sequence of 7 to 40 hexadecimal digits will be recognized as a
55 1
    commit SHA. The minimum of 7 digits is not an arbitrary choice, it
56
    is the default length for short SHAs in Git.
57 1
    """
58
    re.match('^[0-9a-f]{7,40}$', rev) is not None
0 ignored issues
show
Unused Code Bug introduced by
The expression re.match('^[0-9a-f]{7,40}$', rev) is not None does not seem to have sideeffects and its result is not used.

If a expression has no sideeffects (any lasting effect after it has been called) and its return value is not used, this usually means that this code can be removed or that an assignment is missing.

Loading history...
59
60 1
def fetch(repo, rev=None):
61
    """Fetch the latest changes from the remote repository."""
62 1
    git('remote', 'set-url', 'origin', repo)
63
    args = ['fetch', '--tags', '--force', '--prune', 'origin']
64 1
    if rev:
65
        if is_sha(rev):
66 1
            pass  # fetch only works with a SHA if already present locally
67
        elif '@' in rev:
68
            pass  # fetch doesn't work with rev-parse
69 1
        else:
70
            args.append(rev)
71
    git(*args)
72 1
73
74 1
def valid():
75 1
    """Confirm the current directory is a valid working tree."""
76
    log.debug("Checking for a valid working tree...")
77
78 1
    try:
79
        git('rev-parse', '--is-inside-work-tree', _show=False)
80 1
    except ShellError:
81 1
        return False
82 1
    else:
83 1
        return True
84
85 1
86
def changes(include_untracked=False, display_status=True, _show=False):
87
    """Determine if there are changes in the working tree."""
88 1
    status = False
89
90 1
    try:
91
        # Refresh changes
92 1
        git('update-index', '-q', '--refresh', _show=False)
93 1
94 1
        # Check for uncommitted changes
95
        git('diff-index', '--quiet', 'HEAD', _show=_show)
96 1
97 1
        # Check for untracked files
98 1
        lines = git('ls-files', '--others', '--exclude-standard', _show=_show)
99
100 1
    except ShellError:
101
        status = True
102 1
103
    else:
104
        status = bool(lines) and include_untracked
105 1
106
    if status and display_status:
107 1
        with suppress(ShellError):
108
            lines = git('status', _show=True)
109
            common.show(*lines, color='git_changes')
110 1
111
    return status
112 1
113
114
def update(rev, *, clean=True, fetch=False):  # pylint: disable=redefined-outer-name
115 1
    """Update the working tree to the specified revision."""
116
    hide = {'_show': False, '_ignore': True}
117 1
118
    git('stash', **hide)
119
    if clean:
120
        git('clean', '--force', '-d', '-x', _show=False)
121 1
122
    rev = _get_sha_from_rev(rev)
123 1
    git('checkout', '--force', rev)
124
    git('branch', '--set-upstream-to', 'origin/' + rev, **hide)
125
126 1
    if fetch:
127
        # if `rev` was a branch it might be tracking something older
128 1
        git('pull', '--ff-only', '--no-rebase', **hide)
129 1
130 1
131 1
def get_url():
132 1
    """Get the current repository's URL."""
133 1
    return git('config', '--get', 'remote.origin.url', _show=False)[0]
134
135 1
136
def get_hash(_show=False):
137
    """Get the current working tree's hash."""
138
    return git('rev-parse', 'HEAD', _show=_show)[0]
139
140
141
def get_tag():
142
    """Get the current working tree's tag (if on a tag)."""
143
    return git('describe', '--tags', '--exact-match',
144
               _show=False, _ignore=True)[0]
145
146
147
def get_branch():
148
    """Get the current working tree's branch."""
149
    return git('rev-parse', '--abbrev-ref', 'HEAD', _show=False)[0]
150
151
152
def _get_sha_from_rev(rev):
153
    """Get a rev-parse string's hash."""
154
    if '@{' in rev:  # TODO: use regex for this
155
        parts = rev.split('@')
156
        branch = parts[0]
157
        date = parts[1].strip("{}")
158
        git('checkout', '--force', branch, _show=False)
159
        rev = git('rev-list', '-n', '1', '--before={!r}'.format(date),
160
                  branch, _show=False)[0]
161
    return rev
162