@@ -364,74 +364,116 @@ def diff(self, top_repo_path):
364
364
365
365
def branch (self , current_path ):
366
366
"""
367
- Execute 'git show -ref' command & return the result.
367
+ Execute 'git for-each -ref' command & return the result.
368
368
"""
369
+ heads = self .branch_heads (current_path )
370
+ if heads ["code" ] != 0 :
371
+ # error; bail
372
+ return heads
373
+
374
+ remotes = self .branch_remotes (current_path )
375
+ if remotes ["code" ] != 0 :
376
+ # error; bail
377
+ return remotes
378
+
379
+ # all's good; concatenate results and return
380
+ return {"code" : 0 , "branches" : heads ["branches" ] + remotes ["branches" ]}
381
+
382
+ def branch_heads (self , current_path ):
383
+ """
384
+ Execute 'git for-each-ref' command on refs/heads & return the result.
385
+ """
386
+ formats = ['refname:short' , 'objectname' , 'upstream:short' , 'HEAD' ]
387
+ cmd = ["git" , "for-each-ref" , "--format=" + "%09" .join ("%({})" .format (f ) for f in formats ), "refs/heads/" ]
369
388
p = subprocess .Popen (
370
- [ "git" , "show-ref" ] ,
389
+ cmd ,
371
390
stdout = PIPE ,
372
391
stderr = PIPE ,
373
392
cwd = os .path .join (self .root_dir , current_path ),
374
393
)
375
394
output , error = p .communicate ()
376
395
if p .returncode == 0 :
396
+ current_branch_seen = False
377
397
results = []
378
398
try :
379
- current_branch = self .get_current_branch (current_path )
380
- for line in output .decode ("utf-8" ).splitlines ():
381
- # The format for git show-ref is '<SHA-1 ID> <space> <reference name>'
382
- # For this method we are only interested in reference name.
383
- # Reference : https://git-scm.com/docs/git-show-ref#_output
384
- commit_sha = line .strip ().split ()[0 ].strip ()
385
- reference_name = line .strip ().split ()[1 ].strip ()
386
- if self ._is_branch (reference_name ):
387
- branch_name = self ._get_branch_name (reference_name )
388
- is_current_branch = self ._is_current_branch (
389
- branch_name , current_branch
390
- )
391
- is_remote_branch = self ._is_remote_branch (reference_name )
392
- upstream_branch_name = None
393
- if not is_remote_branch :
394
- upstream_branch_name = self .get_upstream_branch (
395
- current_path , branch_name
396
- )
397
- tag = self ._get_tag (current_path , commit_sha )
398
- results .append (
399
- {
400
- "is_current_branch" : is_current_branch ,
401
- "is_remote_branch" : is_remote_branch ,
402
- "name" : branch_name ,
403
- "upstream" : upstream_branch_name ,
404
- "top_commit" : commit_sha ,
405
- "tag" : tag ,
406
- }
407
- )
399
+ for name ,commit_sha ,upstream_name ,is_current_branch in (line .split ('\t ' ) for line in output .decode ("utf-8" ).splitlines ()):
400
+ # Format reference : https://git-scm.com/docs/git-for-each-ref#_field_names
401
+ is_current_branch = bool (is_current_branch .strip ())
402
+ current_branch_seen |= is_current_branch
403
+
404
+ results .append ({
405
+ "is_current_branch" : is_current_branch ,
406
+ "is_remote_branch" : False ,
407
+ "name" : name ,
408
+ "upstream" : upstream_name if upstream_name else None ,
409
+ "top_commit" : commit_sha ,
410
+ "tag" : None ,
411
+ })
408
412
409
413
# Remote branch is seleted use 'git branch -a' as fallback machanism
410
414
# to get add detached head on remote branch to preserve older functionality
411
415
# TODO : Revisit this to checkout new local branch with same name as remote
412
416
# when the remote branch is seleted, VS Code git does the same thing.
413
- if current_branch == "HEAD" :
414
- results .append (
415
- {
416
- "is_current_branch" : True ,
417
- "is_remote_branch" : False ,
418
- "name" : self ._get_detached_head_name (current_path ),
419
- "upstream" : None ,
420
- "top_commit" : None ,
421
- "tag" : None ,
422
- }
423
- )
417
+ if not current_branch_seen and self .get_current_branch (current_path ) == "HEAD" :
418
+ results .append ({
419
+ "is_current_branch" : True ,
420
+ "is_remote_branch" : False ,
421
+ "name" : self ._get_detached_head_name (current_path ),
422
+ "upstream" : None ,
423
+ "top_commit" : None ,
424
+ "tag" : None ,
425
+ })
426
+ return {"code" : p .returncode , "branches" : results }
427
+ except Exception as downstream_error :
428
+ return {
429
+ "code" : - 1 ,
430
+ "command" : ' ' .join (cmd ),
431
+ "message" : str (downstream_error ),
432
+ }
433
+ else :
434
+ return {
435
+ "code" : p .returncode ,
436
+ "command" : ' ' .join (cmd ),
437
+ "message" : error .decode ("utf-8" ),
438
+ }
439
+
440
+ def branch_remotes (self , current_path ):
441
+ """
442
+ Execute 'git for-each-ref' command on refs/heads & return the result.
443
+ """
444
+ formats = ['refname:short' , 'objectname' ]
445
+ cmd = ["git" , "for-each-ref" , "--format=" + "%09" .join ("%({})" .format (f ) for f in formats ), "refs/remotes/" ]
446
+ p = subprocess .Popen (
447
+ cmd ,
448
+ stdout = PIPE ,
449
+ stderr = PIPE ,
450
+ cwd = os .path .join (self .root_dir , current_path ),
451
+ )
452
+ output , error = p .communicate ()
453
+ if p .returncode == 0 :
454
+ results = []
455
+ try :
456
+ for name ,commit_sha in (line .split ('\t ' ) for line in output .decode ("utf-8" ).splitlines ()):
457
+ # Format reference : https://git-scm.com/docs/git-for-each-ref#_field_names
458
+ results .append ({
459
+ "is_current_branch" : False ,
460
+ "is_remote_branch" : True ,
461
+ "name" : name ,
462
+ "upstream" : None ,
463
+ "top_commit" : commit_sha ,
464
+ "tag" : None ,
465
+ })
424
466
return {"code" : p .returncode , "branches" : results }
425
467
except Exception as downstream_error :
426
468
return {
427
- "code" : p . returncode ,
428
- "command" : "git show-ref" ,
469
+ "code" : - 1 ,
470
+ "command" : ' ' . join ( cmd ) ,
429
471
"message" : str (downstream_error ),
430
472
}
431
473
else :
432
474
return {
433
475
"code" : p .returncode ,
434
- "command" : "git show-ref" ,
476
+ "command" : ' ' . join ( cmd ) ,
435
477
"message" : error .decode ("utf-8" ),
436
478
}
437
479
0 commit comments