-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgitstatus.py
134 lines (118 loc) · 4.79 KB
/
gitstatus.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# change those symbols to whatever you prefer
symbols = {'ahead of': '↑·', 'behind': '↓·', 'prehash':':'}
import os
import subprocess
from subprocess import Popen, PIPE
import sys
import json
def _Popen(cmd, folder, stdout=None, stderr=None):
startupinfo = None
if os.name == 'nt':
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
return subprocess.Popen(cmd, stdout=stdout, stderr=stderr, cwd=folder, startupinfo=startupinfo)
def git_repo_info(folder, git_command='git'):
gitsym = _Popen([git_command, 'symbolic-ref', 'HEAD'], folder, stdout=PIPE, stderr=PIPE)
branch, error = gitsym.communicate()
error_string = error.decode('utf-8')
if 'fatal: Not a git repository' in error_string:
raise Exception(error_string)
branch = branch.decode('utf-8').strip()[11:]
remote_name = _Popen([git_command,'config','branch.%s.remote' % branch], folder, stdout=PIPE).communicate()[0].strip()
if not remote_name:
remote_name = None
return {
"path": folder,
"remote": remote_name,
"branch": branch
}
def gitstatus(folder, git_command='git', plain_only=False):
if (plain_only):
status_plain = _Popen([git_command, 'status'], folder, stdout=PIPE).communicate()[0]
sha = _Popen([git_command,'rev-parse', 'HEAD'], folder, stdout=PIPE).communicate()[0].strip()
return {
"status": status_plain,
"sha": sha
}
gitsym = _Popen([git_command, 'symbolic-ref', 'HEAD'], folder, stdout=PIPE, stderr=PIPE)
branch, error = gitsym.communicate()
error_string = error.decode('utf-8')
if 'fatal: Not a git repository' in error_string:
return None
branch = branch.decode('utf-8').strip()[11:]
res, err = _Popen([git_command, 'diff', '--name-status'], folder, stdout=PIPE, stderr=PIPE).communicate()
err_string = err.decode('utf-8')
if 'fatal' in err_string:
return None
changed_files = [namestat[0] for namestat in res.splitlines()]
staged_files = [namestat[0] for namestat in _Popen([git_command,'diff', '--staged','--name-status'], folder, stdout=PIPE).communicate()[0].splitlines()]
nb_changed = len(changed_files) - changed_files.count('U')
nb_U = staged_files.count('U')
nb_staged = len(staged_files) - nb_U
staged = nb_staged
conflicts = nb_U
changed = nb_changed
status = _Popen([git_command,'status','-s','-uall'], folder, stdout=PIPE).communicate()[0]
status_lines = status.splitlines()
untracked_lines = [a for a in map(lambda s: s.decode('utf-8'), status_lines) if a.startswith("??")]
nb_untracked = len(untracked_lines)
untracked = nb_untracked
stashes = _Popen([git_command,'stash','list'], folder, stdout=PIPE).communicate()[0].splitlines()
nb_stashed = len(stashes)
stashed = nb_stashed
if not nb_changed and not nb_staged and not nb_U and not nb_untracked and not nb_stashed:
clean = True
else:
clean = False
remote = ''
tag, tag_error = _Popen([git_command, 'describe', '--exact-match'], folder, stdout=PIPE, stderr=PIPE).communicate()
sha = _Popen([git_command,'rev-parse','HEAD'], folder, stdout=PIPE).communicate()[0].strip()
if not branch: # not on any branch
if tag: # if we are on a tag, print the tag's name
branch = tag
else:
branch = symbols['prehash'] + sha.decode('utf-8')[:-1]
else:
remote_name = _Popen([git_command,'config','branch.%s.remote' % branch], folder, stdout=PIPE).communicate()[0].strip()
if remote_name:
merge_name = _Popen([git_command,'config','branch.%s.merge' % branch], folder, stdout=PIPE).communicate()[0].strip()
else:
remote_name = "origin"
merge_name = "refs/heads/%s" % branch
if remote_name == '.': # local
remote_ref = merge_name
else:
remote_ref = 'refs/remotes/%s/%s' % (remote_name, merge_name[11:])
revgit = _Popen([git_command, 'rev-list', '--left-right', '%s...HEAD' % remote_ref], folder, stdout=PIPE, stderr=PIPE)
revlist = revgit.communicate()[0]
if revgit.poll(): # fallback to local
revlist = _Popen([git_command, 'rev-list', '--left-right', '%s...HEAD' % merge_name], folder, stdout=PIPE, stderr=PIPE).communicate()[0]
behead = revlist.splitlines()
ahead = len([x for x in behead if x[0]=='>'])
behind = len(behead) - ahead
if behind:
remote += '%s%s' % (symbols['behind'], behind)
if ahead:
remote += '%s%s' % (symbols['ahead of'], ahead)
if remote == "":
remote = '.'
result = {
"remote": str(remote_name),
"branch": str(branch),
"sha": str(sha),
"ahead": ahead,
"behind": behind,
"staged": staged,
"conflicts": conflicts,
"changed": changed,
"untracked": untracked,
"stashed": stashed,
"clean": clean,
"status": status
}
return result
if __name__ == "__main__":
stat = gitstatus(os.getcwd())
print(json.dumps(stat))