1#!/usr/bin/env python 2from __future__ import print_function 3 4import os 5import sys 6import re 7from subprocess import Popen, PIPE, check_output 8 9 10def get_tagname_or_hash(): 11 """return tagname if exists else hash""" 12 # get hash 13 hash_cmd = ['git', 'rev-parse', '--short', 'HEAD'] 14 hash_ = check_output(hash_cmd).decode('utf-8').strip() 15 16 # get tagname 17 tags_cmd = ['git', 'for-each-ref', '--points-at=HEAD', '--count=2', '--sort=-version:refname', '--format=%(refname:short)', 'refs/tags'] 18 tags = check_output(tags_cmd).decode('utf-8').split() 19 20 if tags: 21 return tags[0] + ('+' if len(tags) > 1 else '') 22 elif hash_: 23 return hash_ 24 return None 25 26# Re-use method from https://github.com/magicmonty/bash-git-prompt to get stashs count 27def get_stash(): 28 cmd = Popen(['git', 'rev-parse', '--git-dir'], stdout=PIPE, stderr=PIPE) 29 so, se = cmd.communicate() 30 stash_file = '%s%s' % (so.decode('utf-8').rstrip(), '/logs/refs/stash') 31 32 try: 33 with open(stash_file) as f: 34 return sum(1 for _ in f) 35 except IOError: 36 return 0 37 38 39# `git status --porcelain --branch` can collect all information 40# branch, remote_branch, untracked, staged, changed, conflicts, ahead, behind 41po = Popen(['git', 'status', '--porcelain', '--branch'], env=dict(os.environ, LANG="C"), stdout=PIPE, stderr=PIPE) 42stdout, sterr = po.communicate() 43if po.returncode != 0: 44 sys.exit(0) # Not a git repository 45 46# collect git status information 47untracked, staged, changed, conflicts = [], [], [], [] 48ahead, behind = 0, 0 49status = [(line[0], line[1], line[2:]) for line in stdout.decode('utf-8').splitlines()] 50for st in status: 51 if st[0] == '#' and st[1] == '#': 52 if re.search('Initial commit on', st[2]) or re.search('No commits yet on', st[2]): 53 branch = st[2].split(' ')[-1] 54 elif re.search('no branch', st[2]): # detached status 55 branch = get_tagname_or_hash() 56 elif len(st[2].strip().split('...')) == 1: 57 branch = st[2].strip() 58 else: 59 # current and remote branch info 60 branch, rest = st[2].strip().split('...') 61 if len(rest.split(' ')) == 1: 62 # remote_branch = rest.split(' ')[0] 63 pass 64 else: 65 # ahead or behind 66 divergence = ' '.join(rest.split(' ')[1:]) 67 divergence = divergence.lstrip('[').rstrip(']') 68 for div in divergence.split(', '): 69 if 'ahead' in div: 70 ahead = int(div[len('ahead '):].strip()) 71 elif 'behind' in div: 72 behind = int(div[len('behind '):].strip()) 73 elif st[0] == '?' and st[1] == '?': 74 untracked.append(st) 75 else: 76 if st[1] == 'M': 77 changed.append(st) 78 if st[0] == 'U': 79 conflicts.append(st) 80 elif st[0] != ' ': 81 staged.append(st) 82 83stashed = get_stash() 84if not changed and not staged and not conflicts and not untracked and not stashed: 85 clean = 1 86else: 87 clean = 0 88 89out = ' '.join([ 90 branch, 91 str(ahead), 92 str(behind), 93 str(len(staged)), 94 str(len(conflicts)), 95 str(len(changed)), 96 str(len(untracked)), 97 str(stashed), 98 str(clean) 99]) 100print(out, end='') 101