Completed
Pull Request — develop (#168)
by
unknown
03:06
created

gitsvn()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
1
"""Utilities to call Git commands."""
2
3 1
import os
4 1
import shutil
5 1
6
import logging
7 1
from contextlib import suppress
8 1
import re
9 1
10
from . import common, settings
11
from .shell import call
12 1
from .exceptions import ShellError
13
14
15 1
log = logging.getLogger(__name__)
16 1
17
18
def git(*args, **kwargs):
19 1
    return call('git', *args, **kwargs)
20
21 1
def gitsvn(*args, **kwargs):
22
    return call('git', 'svn', *args, **kwargs) 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
23 1
24 1
def clone(type, repo, path, *, cache=settings.CACHE, sparse_paths=None, rev=None):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
25 1
    """Clone a new Git repository."""
26
    log.debug("Creating a new repository...")
27 1
28 1
    if type == 'git-svn':
29 1
        # just the preperation for the svn deep clone / checkout here (clone will be made in update function to simplify source.py).
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (132/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
30
        os.makedirs(path)
31 1
        return
32
33
    assert type == 'git'
34 1
35
    name = repo.split('/')[-1]
36 1
    if name.endswith(".git"):
37 1
        name = name[:-4]
38 1
39 1
    reference = os.path.join(cache, name + ".reference")
40 1
    if not os.path.isdir(reference):
41 1
        git('clone', '--mirror', repo, reference)
42 1
43
    normpath = os.path.normpath(path)
44 1
    if sparse_paths:
45 1
        os.mkdir(normpath)
46
        git('-C', normpath, 'init')
47
        git('-C', normpath, 'config', 'core.sparseCheckout', 'true')
48 1
        git('-C', normpath, 'remote', 'add', '-f', 'origin', reference)
49
50 1
        with open("%s/%s/.git/info/sparse-checkout" % (os.getcwd(), normpath), 'w') as fd:
51
            fd.writelines(sparse_paths)
52 1
        with open("%s/%s/.git/objects/info/alternates" % (os.getcwd(), normpath), 'w') as fd:
53 1
            fd.write("%s/objects" % reference)
54 1
55 1
        # We use directly the revision requested here in order to respect,
56
        # that not all repos have `master` as their default branch
57 1
        git('-C', normpath, 'pull', 'origin', rev)
58
    else:
59
        git('clone', '--reference', reference, repo, os.path.normpath(path))
60 1
61
62 1
63
def is_sha(rev):
64 1
    """Heuristically determine whether a revision corresponds to a commit SHA.
65
66 1
    Any sequence of 7 to 40 hexadecimal digits will be recognized as a
67
    commit SHA. The minimum of 7 digits is not an arbitrary choice, it
68
    is the default length for short SHAs in Git.
69 1
    """
70
    return re.match('^[0-9a-f]{7,40}$', rev) is not None
71
72 1
73
def fetch(type, repo, path, rev=None):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Unused Code introduced by
The argument path seems to be unused.
Loading history...
74 1
    """Fetch the latest changes from the remote repository."""
75 1
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
76
    if type == 'git-svn':
77
        # deep clone happens in update function
78 1
        return
79
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
80 1
    assert type == 'git'
81 1
82 1
    git('remote', 'set-url', 'origin', repo)
83 1
    args = ['fetch', '--tags', '--force', '--prune', 'origin']
84
    if rev:
85 1
        if is_sha(rev):
86
            pass  # fetch only works with a SHA if already present locally
87
        elif '@' in rev:
88 1
            pass  # fetch doesn't work with rev-parse
89
        else:
90 1
            args.append(rev)
91
    git(*args)
92 1
93 1
94 1
def valid():
95
    """Confirm the current directory is a valid working tree."""
96 1
    log.debug("Checking for a valid working tree...")
97 1
98 1
    try:
99
        git('rev-parse', '--is-inside-work-tree', _show=False)
100 1
    except ShellError:
101
        return False
102 1
    else:
103
        return True
104
105 1
106
def changes(type, include_untracked=False, display_status=True, _show=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
107 1
    """Determine if there are changes in the working tree."""
108
    status = False
109
110 1
    if type == 'git-svn':
111
        # ignore changes in case of git-svn
112 1
        return status
113
114
    assert type == 'git'
115 1
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
116
    try:
117 1
        # Refresh changes
118
        git('update-index', '-q', '--refresh', _show=False)
119
120
        # Check for uncommitted changes
121 1
        git('diff-index', '--quiet', 'HEAD', _show=_show)
122
123 1
        # Check for untracked files
124
        lines = git('ls-files', '--others', '--exclude-standard', _show=_show)
125
126 1
    except ShellError:
127
        status = True
128 1
129 1
    else:
130 1
        status = bool(lines) and include_untracked
131 1
132 1
    if status and display_status:
133 1
        with suppress(ShellError):
134
            lines = git('status', _show=True)
135 1
            common.show(*lines, color='git_changes')
136
137
    return status
138
139
140
def update(type, repo, path, *, clean=True, fetch=False, rev=None):  # pylint: disable=redefined-outer-name
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Unused Code introduced by
The argument path seems to be unused.
Loading history...
141
 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
142
    if type == 'git-svn':
143
        # make deep clone here for simplification of sources.py
144
        # and to realize consistent readonly clone (always forced)
145
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
146
        # completly empty current directory (remove also hidden content)
147
        for root, dirs, files in os.walk('.'):
148
            for f in files:
149
                os.unlink(os.path.join(root, f))
150
            for d in dirs:
151
                shutil.rmtree(os.path.join(root, d))
152
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
153
        # clone specified svn revision 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
154
        gitsvn('clone', '-r', rev, repo, '.')
155
        return
156
157
    assert type == 'git'
158
159
    """Update the working tree to the specified revision."""
0 ignored issues
show
Unused Code introduced by
This string statement has no effect and could be removed.
Loading history...
160
    hide = {'_show': False, '_ignore': True}
161
162
    git('stash', **hide)
163
    if clean:
164
        git('clean', '--force', '-d', '-x', _show=False)
165
166
    rev = _get_sha_from_rev(rev)
167
    git('checkout', '--force', rev)
168
    git('branch', '--set-upstream-to', 'origin/' + rev, **hide)
169
170
    if fetch:
171
        # if `rev` was a branch it might be tracking something older
172
        git('pull', '--ff-only', '--no-rebase', **hide)
173
174
175
def get_url(type):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
176
    """Get the current repository's URL."""
177
    if type == 'git-svn':
178
        return git('config', '--get', 'svn-remote.svn.url', _show=False)[0]
179
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
180
    assert type == 'git'
181
182
    return git('config', '--get', 'remote.origin.url', _show=False)[0]
183
184
185
def get_hash(type, _show=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
186
    """Get the current working tree's hash."""
187
    if type == 'git-svn':
188
        return ''.join(filter(str.isdigit, gitsvn('info', _show=_show)[4]))
189
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
190
    assert type == 'git'
191
    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
192
    return git('rev-parse', 'HEAD', _show=_show)[0]
193
194
def get_tag():
195
    """Get the current working tree's tag (if on a tag)."""
196
    return git('describe', '--tags', '--exact-match',
197
               _show=False, _ignore=True)[0]
198
199
def is_fetch_required(type, rev):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
200
    if type == 'git-svn':
201
        return False
202
   
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
203
    assert type == 'git'
204
205
    return rev not in (get_branch(),
206
                        get_hash(type),
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 1 space).
Loading history...
207
                        get_tag())
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 1 space).
Loading history...
208
209
210
def get_branch():
211
    """Get the current working tree's branch."""
212
    return git('rev-parse', '--abbrev-ref', 'HEAD', _show=False)[0]
213
214
215
def _get_sha_from_rev(rev):
216
    """Get a rev-parse string's hash."""
217
    if '@{' in rev:  # TODO: use regex for this
218
        parts = rev.split('@')
219
        branch = parts[0]
220
        date = parts[1].strip("{}")
221
        git('checkout', '--force', branch, _show=False)
222
        rev = git('rev-list', '-n', '1', '--before={!r}'.format(date),
223
                  branch, _show=False)[0]
224
    return rev
225