forked from Rosuav/shed
-
Notifications
You must be signed in to change notification settings - Fork 0
/
git-watch
executable file
·93 lines (84 loc) · 3.3 KB
/
git-watch
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
#!/usr/bin/env python3
"""
Watch git repositories for uncommitted or unpushed changes
Suggestion: Have notifications automatically emailed to you.
git watch | mail user@domain.example -s "Status of git repos" -E
Further suggestion: Set up a cron job or equivalent to do the
above regularly (eg daily).
"""
import argparse
import os
import sys
import subprocess
commands = []
def command(f):
commands.append(f)
return f
# First command is also the default
@command
def check(repos):
"""Check all repositories on your list"""
dirs = subprocess.check_output(["git", "config", "--get-all", "rosuav.git-watch.repos"])
dirs = dirs.decode("ascii").strip("\n").split("\n")
for dir in dirs:
try:
status = subprocess.check_output(["git", "-C", dir, "status", "--porcelain", "--branch"])
except subprocess.CalledProcessError as e:
# It's entirely possible that stderr isn't connected anywhere,
# so give an admin a chance to see that there's a problem.
print(dir, "-- unable to 'git status', see stderr")
continue
status = status.decode("ascii").strip("\n").split("\n")
if not status:
# Completely empty??? Probably some sort of error.
print(dir, "-- unexpected situation, please contact author")
continue
# First line should be the branch info.
if "..." not in status[0]:
print(dir, "-- no upstream for branch, suggest 'git push -u'")
if "[ahead" in status[0]:
# Note that diverged branches show "[ahead N, behind m]"
print(dir, "-- has unpushed commits")
if len(status) > 1:
print(dir, "-- has unstaged changes")
@command
def add(repos):
"""Add this or another repository to the watch list"""
# TODO: Add a 'verbose mode'
repos = repos or ["."]
for repo in repos:
dir = subprocess.check_output(["git", "-C", repo, "rev-parse", "--show-toplevel"])
dir = dir.decode("ascii").strip("\n")
# TODO: Ensure that 'dir' doesn't have any regex-special characters, or escape them
subprocess.check_call(["git", "config", "--global", "--replace-all", "rosuav.git-watch.repos", dir, dir])
# TODO: 'search' subcommand to scan the file system for .git directories??
@command
def rm(repos):
"""Remove this or another repository from the watch list"""
# TODO: Add a 'verbose mode'
repos = repos or ["."]
for repo in repos:
try:
dir = subprocess.check_output(["git", "-C", repo, "rev-parse", "--show-toplevel"])
dir = dir.decode("ascii").strip("\n")
except subprocess.CalledProcessError:
# TODO: If the dir isn't found in config, error out.
# (If it IS found, remove it, as that's how you remove a deleted repo.)
dir = os.path.abspath(repo)
# TODO: As above, check for regex specials
subprocess.check_call(["git", "config", "--global", "--unset-all", "rosuav.git-watch.repos", dir])
def main():
parser = argparse.ArgumentParser(description=__doc__.split("\n")[0])
subparsers = parser.add_subparsers(dest="command")
for func in commands:
par = subparsers.add_parser(func.__name__, help=func.__doc__.split("\n")[0])
par.set_defaults(func=func)
par.add_argument("repos", nargs="*", help="Repository(ies) to check")
args = parser.parse_args()
if not args.command:
# In Pythons older than 3.4.3, we can't just set_defaults for these,
# so we hack around it by stashing a command name in there.
return commands[0]([])
return args.func(args.repos)
if __name__ == '__main__':
sys.exit(main())