|
3 | 3 | import logging
|
4 | 4 | log = logging.getLogger('git_repo.bitbucket')
|
5 | 5 |
|
6 |
| -from ..service import register_target, RepositoryService |
| 6 | +from ..service import register_target, RepositoryService, ProgressBar |
7 | 7 | from ...exceptions import ResourceError, ResourceExistsError, ResourceNotFoundError
|
| 8 | +from ...tools import columnize |
8 | 9 |
|
9 | 10 | from pybitbucket.bitbucket import Client, Bitbucket
|
10 | 11 | from pybitbucket.auth import BasicAuthenticator
|
|
18 | 19 |
|
19 | 20 | from requests import Request, Session
|
20 | 21 | from requests.exceptions import HTTPError
|
| 22 | + |
| 23 | +from git.exc import GitCommandError |
| 24 | + |
21 | 25 | from lxml import html
|
22 | 26 | import os, json, platform
|
23 | 27 |
|
@@ -92,65 +96,46 @@ def delete(self, repo, user=None):
|
92 | 96 | raise ResourceError("Couldn't complete deletion: {}".format(err)) from err
|
93 | 97 |
|
94 | 98 | def list(self, user, _long=False):
|
95 |
| - import shutil, sys |
96 |
| - from datetime import datetime |
97 |
| - term_width = shutil.get_terminal_size((80, 20)).columns |
98 |
| - def col_print(lines, indent=0, pad=2): |
99 |
| - # prints a list of items in a fashion similar to the dir command |
100 |
| - # borrowed from https://gist.github.com/critiqjo/2ca84db26daaeb1715e1 |
101 |
| - n_lines = len(lines) |
102 |
| - if n_lines == 0: |
103 |
| - return |
104 |
| - col_width = max(len(line) for line in lines) |
105 |
| - n_cols = int((term_width + pad - indent)/(col_width + pad)) |
106 |
| - n_cols = min(n_lines, max(1, n_cols)) |
107 |
| - col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1) |
108 |
| - if (n_cols - 1) * col_len >= n_lines: |
109 |
| - n_cols -= 1 |
110 |
| - cols = [lines[i*col_len : i*col_len + col_len] for i in range(n_cols)] |
111 |
| - rows = list(zip(*cols)) |
112 |
| - rows_missed = zip(*[col[len(rows):] for col in cols[:-1]]) |
113 |
| - rows.extend(rows_missed) |
114 |
| - for row in rows: |
115 |
| - print(" "*indent + (" "*pad).join(line.ljust(col_width) for line in row)) |
116 |
| - |
117 | 99 | try:
|
118 | 100 | user = User.find_user_by_username(user)
|
119 | 101 | except HTTPError as err:
|
120 | 102 | raise ResourceNotFoundError("User {} does not exists.".format(user)) from err
|
121 | 103 |
|
122 | 104 | repositories = user.repositories()
|
123 | 105 | if not _long:
|
| 106 | + yield "{}" |
124 | 107 | repositories = list(repositories)
|
125 |
| - col_print(["/".join([user.username, repo.name]) for repo in repositories]) |
| 108 | + yield ("Total repositories: {}".format(len(repositories)),) |
| 109 | + yield from columnize(["/".join([user.username, repo.name]) for repo in repositories]) |
126 | 110 | else:
|
127 |
| - print('Status\tCommits\tReqs\tIssues\tForks\tCoders\tWatch\tLikes\tLang\tModif\t\tName', file=sys.stderr) |
| 111 | + yield "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{:12}\t{}" |
| 112 | + yield ['Status', 'Commits', 'Reqs', 'Issues', 'Forks', 'Coders', 'Watch', 'Likes', 'Lang', 'Modif', 'Name'] |
128 | 113 | for repo in repositories:
|
129 | 114 | # if repo.updated_at.year < datetime.now().year:
|
130 | 115 | # date_fmt = "%b %d %Y"
|
131 | 116 | # else:
|
132 | 117 | # date_fmt = "%b %d %H:%M"
|
133 | 118 |
|
134 | 119 | status = ''.join([
|
135 |
| - 'F' if getattr(repo, 'parent', None) else ' ', # is a fork? |
136 |
| - 'P' if repo.is_private else ' ', # is private? |
| 120 | + 'F' if getattr(repo, 'parent', None) else ' ', # is a fork? |
| 121 | + 'P' if repo.is_private else ' ', # is private? |
137 | 122 | ])
|
138 |
| - print('\t'.join([ |
| 123 | + yield [ |
139 | 124 | # status
|
140 | 125 | status,
|
141 | 126 | # stats
|
142 |
| - str(len(list(repo.commits()))), # number of commits |
143 |
| - str(len(list(repo.pullrequests()))), # number of pulls |
144 |
| - str('N.A.'), # number of issues |
145 |
| - str(len(list(repo.forks()))), # number of forks |
146 |
| - str('N.A.'), # number of contributors |
147 |
| - str(len(list(repo.watchers()))), # number of subscribers |
148 |
| - str('N.A.'), # number of ♥ |
| 127 | + str(len(list(repo.commits()))), # number of commits |
| 128 | + str(len(list(repo.pullrequests()))), # number of pulls |
| 129 | + str('N.A.'), # number of issues |
| 130 | + str(len(list(repo.forks()))), # number of forks |
| 131 | + str('N.A.'), # number of contributors |
| 132 | + str(len(list(repo.watchers()))), # number of subscribers |
| 133 | + str('N.A.'), # number of ♥ |
149 | 134 | # info
|
150 |
| - repo.language or '?', # language |
151 |
| - repo.updated_on, # date |
152 |
| - '/'.join([user.username, repo.name]), # name |
153 |
| - ])) |
| 135 | + repo.language or '?', # language |
| 136 | + repo.updated_on, # date |
| 137 | + '/'.join([user.username, repo.name]), # name |
| 138 | + ] |
154 | 139 |
|
155 | 140 | def get_repository(self, user, repo):
|
156 | 141 | try:
|
@@ -314,43 +299,36 @@ def request_list(self, user, repo):
|
314 | 299 | log.warn('Error while fetching request information: {}'.format(pull))
|
315 | 300 |
|
316 | 301 | def request_fetch(self, user, repo, request, pull=False):
|
317 |
| - log.warn('Bitbucket does not support fetching of PR using git. Use this command at your own risk.') |
318 |
| - if 'y' not in input('Are you sure to continue? [yN]> '): |
319 |
| - raise ResourceError('Command aborted.') |
320 | 302 | if pull:
|
321 | 303 | raise NotImplementedError('Pull operation on requests for merge are not yet supported')
|
| 304 | + |
| 305 | + pb = ProgressBar() |
| 306 | + pb.setup(self.name) |
| 307 | + |
322 | 308 | try:
|
323 |
| - repository = self.get_repository(user, repo) |
324 |
| - if self.repository.is_dirty(): |
325 |
| - raise ResourceError('Please use this command after stashing your changes.') |
326 | 309 | local_branch_name = 'requests/bitbucket/{}'.format(request)
|
327 |
| - index = self.repository.index |
328 |
| - log.info('» Fetching pull request {}'.format(request)) |
329 |
| - request = next(bb.repositoryPullRequestByPullRequestId( |
| 310 | + pr = next(self.bb.repositoryPullRequestByPullRequestId( |
330 | 311 | owner=user,
|
331 | 312 | repository_name=repo,
|
332 | 313 | pullrequest_id=request
|
333 | 314 | ))
|
334 |
| - commit = self.repository.rev_parse(request['destination']['commit']['hash']) |
335 |
| - self.repository.head.reference = commit |
336 |
| - log.info('» Creation of requests branch {}'.format(local_branch_name)) |
337 |
| - # create new branch |
338 |
| - head = self.repository.create_head(local_branch_name) |
339 |
| - head.checkout() |
340 |
| - # fetch and apply patch |
341 |
| - log.info('» Fetching and writing the patch in current directory') |
342 |
| - patch = bb.client.session.get(request['links']['diff']['href']).content.decode('utf-8') |
343 |
| - with open('.tmp.patch', 'w') as f: |
344 |
| - f.write(patch) |
345 |
| - log.info('» Applying the patch') |
346 |
| - git.cmd.Git().apply('.tmp.patch', stat=True) |
347 |
| - os.unlink('.tmp.patch') |
348 |
| - log.info('» Going back to original branch') |
349 |
| - index.checkout() # back to former branch |
| 315 | + source_branch = pr.source['branch']['name'] |
| 316 | + source_slug = pr.source['repository']['full_name'] |
| 317 | + source_url = pr.source['repository']['links']['html']['href'] |
| 318 | + remote_name = 'requests/bitbucket/{}'.format(source_slug).replace('/', '-') |
| 319 | + try: |
| 320 | + remote = self.repository.remote(name=remote_name) |
| 321 | + except ValueError: |
| 322 | + remote = self.repository.create_remote(name=remote_name, url=source_url) |
| 323 | + refspec = '{}:{}'.format(source_branch, local_branch_name) |
| 324 | + refs = remote.fetch(refspec, progress=pb) |
| 325 | + for branch in self.repository.branches: |
| 326 | + if branch.name == local_branch_name: |
| 327 | + branch.set_tracking_branch(remote.refs[0]) |
350 | 328 | return local_branch_name
|
351 | 329 | except HTTPError as err:
|
352 | 330 | if '404' in err.args[0].split(' '):
|
353 |
| - raise ResourceNotFoundError("Could not find snippet {}.".format(gist_id)) from err |
| 331 | + raise ResourceNotFoundError('Could not find opened request #{}'.format(request)) from err |
354 | 332 | raise ResourceError("Couldn't delete snippet: {}".format(err)) from err
|
355 | 333 | except GitCommandError as err:
|
356 | 334 | if 'Error when fetching: fatal: ' in err.command[0]:
|
|
0 commit comments