@@ -364,74 +364,116 @@ def diff(self, top_repo_path):
364364
365365 def branch (self , current_path ):
366366 """
367- Execute 'git show -ref' command & return the result.
367+ Execute 'git for-each -ref' command & return the result.
368368 """
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/" ]
369388 p = subprocess .Popen (
370- [ "git" , "show-ref" ] ,
389+ cmd ,
371390 stdout = PIPE ,
372391 stderr = PIPE ,
373392 cwd = os .path .join (self .root_dir , current_path ),
374393 )
375394 output , error = p .communicate ()
376395 if p .returncode == 0 :
396+ current_branch_seen = False
377397 results = []
378398 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+ })
408412
409413 # Remote branch is seleted use 'git branch -a' as fallback machanism
410414 # to get add detached head on remote branch to preserve older functionality
411415 # TODO : Revisit this to checkout new local branch with same name as remote
412416 # 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+ })
424466 return {"code" : p .returncode , "branches" : results }
425467 except Exception as downstream_error :
426468 return {
427- "code" : p . returncode ,
428- "command" : "git show-ref" ,
469+ "code" : - 1 ,
470+ "command" : ' ' . join ( cmd ) ,
429471 "message" : str (downstream_error ),
430472 }
431473 else :
432474 return {
433475 "code" : p .returncode ,
434- "command" : "git show-ref" ,
476+ "command" : ' ' . join ( cmd ) ,
435477 "message" : error .decode ("utf-8" ),
436478 }
437479
0 commit comments