gitstatus.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #!/usr/bin/env python
  2. from __future__ import print_function
  3. import os
  4. import sys
  5. import re
  6. import shlex
  7. from subprocess import Popen, PIPE, check_output
  8. def get_tagname_or_hash():
  9. """return tagname if exists else hash"""
  10. cmd = 'git log -1 --format="%h%d"'
  11. output = check_output(shlex.split(cmd)).decode('utf-8').strip()
  12. hash_, tagname = None, None
  13. # get hash
  14. m = re.search('\(.*\)$', output)
  15. if m:
  16. hash_ = output[:m.start()-1]
  17. # get tagname
  18. m = re.search('tag: .*[,\)]', output)
  19. if m:
  20. tagname = 'tags/' + output[m.start()+len('tag: '): m.end()-1]
  21. if tagname:
  22. return tagname.replace(' ', '')
  23. elif hash_:
  24. return hash_
  25. return None
  26. # `git status --porcelain --branch` can collect all information
  27. # branch, remote_branch, untracked, staged, changed, conflicts, ahead, behind
  28. po = Popen(['git', 'status', '--porcelain', '--branch'], env=dict(os.environ, LANG="C"), stdout=PIPE, stderr=PIPE)
  29. stdout, sterr = po.communicate()
  30. if po.returncode != 0:
  31. sys.exit(0) # Not a git repository
  32. # collect git status information
  33. untracked, staged, changed, conflicts = [], [], [], []
  34. ahead, behind = 0, 0
  35. status = [(line[0], line[1], line[2:]) for line in stdout.decode('utf-8').splitlines()]
  36. for st in status:
  37. if st[0] == '#' and st[1] == '#':
  38. if re.search('Initial commit on', st[2]) or re.search('No commits yet on', st[2]):
  39. branch = st[2].split(' ')[-1]
  40. elif re.search('no branch', st[2]): # detached status
  41. branch = get_tagname_or_hash()
  42. elif len(st[2].strip().split('...')) == 1:
  43. branch = st[2].strip()
  44. else:
  45. # current and remote branch info
  46. branch, rest = st[2].strip().split('...')
  47. if len(rest.split(' ')) == 1:
  48. # remote_branch = rest.split(' ')[0]
  49. pass
  50. else:
  51. # ahead or behind
  52. divergence = ' '.join(rest.split(' ')[1:])
  53. divergence = divergence.lstrip('[').rstrip(']')
  54. for div in divergence.split(', '):
  55. if 'ahead' in div:
  56. ahead = int(div[len('ahead '):].strip())
  57. elif 'behind' in div:
  58. behind = int(div[len('behind '):].strip())
  59. elif st[0] == '?' and st[1] == '?':
  60. untracked.append(st)
  61. else:
  62. if st[1] == 'M':
  63. changed.append(st)
  64. if st[0] == 'U':
  65. conflicts.append(st)
  66. elif st[0] != ' ':
  67. staged.append(st)
  68. out = ' '.join([
  69. branch,
  70. str(ahead),
  71. str(behind),
  72. str(len(staged)),
  73. str(len(conflicts)),
  74. str(len(changed)),
  75. str(len(untracked)),
  76. ])
  77. print(out, end='')