2
2
3
3
'''
4
4
Usage:
5
- {self} [--path=<path>] [-v...] <target> fork [<branch>] [--clone ]
5
+ {self} [--path=<path>] [-v...] <target> fork [--branch= <branch>]
6
6
{self} [--path=<path>] [-v...] <target> create [--add]
7
7
{self} [--path=<path>] [-v...] <target> delete [-f]
8
8
{self} [--path=<path>] [-v...] <target> open
9
- {self} [--path=<path>] [-v...] <target> fork <user>/<repo> [<branch>] [--clone]
9
+ {self} [--path=<path>] [-v...] <target> fork <user>/<repo> [--branch=<branch>]
10
+ {self} [--path=<path>] [-v...] <target> fork <user>/<repo> <repo> [--branch=<branch>]
10
11
{self} [--path=<path>] [-v...] <target> create <user>/<repo> [--add]
11
12
{self} [--path=<path>] [-v...] <target> delete <user>/<repo> [-f]
12
13
{self} [--path=<path>] [-v...] <target> open <user>/<repo>
13
- {self} [--path=<path>] [-v...] <target> clone <user>/<repo> [<branch>]
14
+ {self} [--path=<path>] [-v...] <target> clone <user>/<repo> [<repo> [< branch>] ]
14
15
{self} [--path=<path>] [-v...] <target> add <user>/<repo> [<name>] [--tracking=<branch>] [-a]
15
16
{self} [--path=<path>] [-v...] <target> request (list|ls)
16
17
{self} [--path=<path>] [-v...] <target> request fetch <request>
55
56
-t,--tracking=<branch> Makes this remote tracking for the current branch
56
57
-a,--alone Does not add the remote to the 'all' remote
57
58
58
- Options for fork and clone:
59
- <branch> Branch to pull (when cloning) [default: master]
60
- --clone Clone locally after fork
59
+ Option for both clone and fork:
60
+ <repo> Name of the local workspace directory
61
+
62
+ Options for clone:
63
+ <branch> Branch to pull [default: master]
64
+
65
+ Options for fork:
66
+ --branch=<branch> Branch to pull [default: master]
61
67
62
68
Options for create:
63
69
--add Add to local repository after creation
128
134
from git import Repo , Git
129
135
from git .exc import InvalidGitRepositoryError , NoSuchPathError
130
136
137
+ import re
138
+
139
+ EXTRACT_URL_RE = re .compile ('[^:]*(://|@)[^/]*/' )
140
+
131
141
def confirm (what , where ):
132
142
'''
133
143
Method to show a CLI based confirmation message, waiting for a yes/no answer.
@@ -155,19 +165,20 @@ def _guess_repo_slug(self, repository, service):
155
165
config = repository .config_reader ()
156
166
target = service .name
157
167
for remote in repository .remotes :
158
- for url in remote .urls :
159
- if url .startswith ('https' ):
160
- if '.git' in url :
161
- url = url [:- 4 ]
162
- * _ , user , name = url .split ('/' )
163
- self .set_repo_slug ('/' .join ([user , name ]))
164
- break
165
- elif url .startswith ('git@' ):
166
- if '.git' in url :
167
- url = url [:- 4 ]
168
- _ , repo_slug = url .split (':' )
169
- self .set_repo_slug (repo_slug )
170
- break
168
+ if remote .name in (target , 'upstream' , 'origin' ):
169
+ for url in remote .urls :
170
+ if url .startswith ('https' ):
171
+ if '.git' in url :
172
+ url = url [:- 4 ]
173
+ * _ , user , name = url .split ('/' )
174
+ self .set_repo_slug ('/' .join ([user , name ]))
175
+ break
176
+ elif url .startswith ('git@' ):
177
+ if '.git' in url :
178
+ url = url [:- 4 ]
179
+ _ , repo_slug = url .split (':' )
180
+ self .set_repo_slug (repo_slug )
181
+ break
171
182
172
183
def get_service (self , lookup_repository = True ):
173
184
if not lookup_repository :
@@ -217,18 +228,19 @@ def set_verbosity(self, verbose): # pragma: no cover
217
228
218
229
@store_parameter ('<user>/<repo>' )
219
230
def set_repo_slug (self , repo_slug ):
220
- self .repo_slug = repo_slug
221
- if not repo_slug :
231
+ self .repo_slug = EXTRACT_URL_RE . sub ( '' , repo_slug ) if repo_slug else repo_slug
232
+ if not self . repo_slug :
222
233
self .user_name = None
223
234
self .repo_name = None
224
- elif '/' in repo_slug :
225
- self .user_name , self .repo_name , * overflow = repo_slug .split ('/' )
235
+ elif '/' in self .repo_slug :
236
+ # in case a full URL is given as parameter, just extract the slug part.
237
+ self .user_name , self .repo_name , * overflow = self .repo_slug .split ('/' )
226
238
if len (overflow ) != 0 :
227
239
raise ArgumentError ('Too many slashes.'
228
240
'Format of the parameter is <user>/<repo> or <repo>.' )
229
241
else :
230
242
self .user_name = None
231
- self .repo_name = repo_slug
243
+ self .repo_name = self . repo_slug
232
244
233
245
@store_parameter ('<branch>' )
234
246
def set_branch (self , branch ):
@@ -238,6 +250,19 @@ def set_branch(self, branch):
238
250
239
251
self .branch = branch
240
252
253
+ @store_parameter ('<branch>' )
254
+ @store_parameter ('--branch' )
255
+ def set_branch (self , branch ):
256
+ # FIXME workaround for default value that is not correctly parsed in docopt
257
+ if branch == None :
258
+ branch = 'master'
259
+
260
+ self .branch = branch
261
+
262
+ @store_parameter ('<repo>' )
263
+ def set_target_repo (self , repo ):
264
+ self .target_repo = repo
265
+
241
266
@store_parameter ('<name>' )
242
267
def set_name (self , name ):
243
268
self .remote_name = name
@@ -267,69 +292,61 @@ def do_remote_add(self):
267
292
268
293
@register_action ('fork' )
269
294
def do_fork (self ):
270
- def clone_repo ():
271
- try :
272
- repo_path = os .path .join (self .path , self .repo_name )
273
- repository = Repo (repo_path )
274
- except (InvalidGitRepositoryError , NoSuchPathError ):
275
- repo_path = os .path .join (self .path , self .repo_name )
276
- repository = Repo .init (repo_path )
277
- return repository , repo_path
278
-
279
- def lookup_repo ():
295
+ if not self .repo_slug :
296
+ service = self .get_service (lookup_repository = True )
280
297
try :
281
298
repo_path = self .path
282
299
repository = Repo (repo_path )
283
300
except (InvalidGitRepositoryError , NoSuchPathError ):
301
+ raise ArgumentError ('Path {} is not a git repository' .format (self .path ))
302
+
303
+ else :
304
+ # git <target> fork <user>/<repo>
305
+ if not self .target_repo :
306
+ if not self .user_name :
307
+ raise ArgumentError ('Cannot clone repository, '
308
+ 'you shall provide either a <user>/<repo> parameter '
309
+ 'or no parameters to fork current repository!' )
310
+ service = self .get_service (None )
311
+
312
+ # git <target> fork <user>/<repo> <path>
313
+ else :
314
+ repo_path = os .path .join (self .path , self .target_repo )
284
315
try :
285
- repo_path = os .path .join (self .path , self .repo_name )
286
- repository = Repo (repo_path )
316
+ service = RepositoryService .get_service (Repo (repo_path ), self .target )
287
317
except (InvalidGitRepositoryError , NoSuchPathError ):
288
- return None , None
289
- return repository , repo_path
318
+ service = self .get_service (lookup_repository = False )
319
+ # if the repository does not exists at given path, clone upstream into that path
320
+ self .do_clone (service , repo_path )
290
321
291
- service = self .get_service (lookup_repository = self .repo_slug == None )
292
- if not self .repo_name and not self .user_name :
293
- raise ArgumentError ('Cannot clone repository, '
294
- 'you shall provide the <user>/<repo> parameter '
295
- 'or run the command from a git repository!' )
296
- if not self .clone :
297
- repository , repo_path = lookup_repo ()
298
- if repository == None :
299
- repository , repo_path = clone_repo ()
300
- self .clone = True
301
- else :
302
- repository , repo_path = clone_repo ()
303
- service = RepositoryService .get_service (repository , self .target )
304
- service .fork (self .user_name , self .repo_name , branch = self .branch , clone = self .clone )
305
- if self .clone :
306
- log .info ('Successfully cloned repository {} in {}' .format (
307
- self .repo_slug ,
308
- repo_path )
309
- )
310
- else :
311
- log .info ('Successfully added repository {} as a remote to {}' .format (
312
- self .repo_slug ,
313
- repo_path )
314
- )
322
+ service .run_fork (self .user_name , self .repo_name , branch = self .branch )
323
+
324
+ if not self .repo_slug or self .target_repo :
325
+ log .info ('Successfully forked {} as {} within {}.' .format (
326
+ self .repo_slug , '/' .join ([service .username , self .repo_name ]), repo_path ))
315
327
316
328
return 0
317
329
318
330
@register_action ('clone' )
319
- def do_clone (self ):
320
- service = self .get_service (lookup_repository = False )
321
- repo_path = os .path .join (self .path , self .repo_name )
331
+ def do_clone (self , service = None , repo_path = None ):
332
+ service = service or self .get_service (lookup_repository = False )
333
+ repo_path = repo_path or os .path .join (self .path , self . target_repo or self .repo_name )
322
334
if os .path .exists (repo_path ):
323
335
raise FileExistsError ('Cannot clone repository, '
324
336
'a folder named {} already exists!' .format (repo_path ))
325
- repository = Repo .init (repo_path )
326
- service = RepositoryService .get_service (repository , self .target )
327
- service .clone (self .user_name , self .repo_name , self .branch )
328
- log .info ('Successfully cloned `{}` into `{}`!' .format (
329
- service .format_path (self .repo_slug ),
330
- repo_path )
331
- )
332
- return 0
337
+ try :
338
+ repository = Repo .init (repo_path )
339
+ service = RepositoryService .get_service (repository , self .target )
340
+ service .clone (self .user_name , self .repo_name , self .branch )
341
+ log .info ('Successfully cloned `{}` into `{}`!' .format (
342
+ service .format_path (self .repo_slug ),
343
+ repo_path )
344
+ )
345
+ return 0
346
+ except Exception as err :
347
+ if os .path .exists (repo_path ):
348
+ os .removedirs (repo_path )
349
+ raise err from err
333
350
334
351
@register_action ('create' )
335
352
def do_create (self ):
0 commit comments