Show NoUpstream error in checkup output.

This commit is contained in:
voussoir 2020-02-19 12:49:30 -08:00
parent e557c1a64f
commit 015957f72d

View file

@ -4,6 +4,7 @@ import subprocess
import sys import sys
import types import types
from voussoirkit import dotdict
from voussoirkit import winwhich from voussoirkit import winwhich
GIT = winwhich.which('git') GIT = winwhich.which('git')
@ -17,19 +18,22 @@ GIT = winwhich.which('git')
# ?? file3 # ?? file3
class NoUpstreamBranch(Exception): class NoUpstreamBranch(Exception):
pass def __str__(self):
return f'No upstream branch for {self.args[0]}'
def check_output(command): def check_output(command):
output = subprocess.check_output(command, stderr=subprocess.STDOUT) output = subprocess.check_output(command, stderr=subprocess.STDOUT)
return output return output
def checkup_committed(): def checkup_committed():
details = dotdict.DotDict(default=None)
command = [GIT, 'status', '--short', '--untracked-files=all'] command = [GIT, 'status', '--short', '--untracked-files=all']
output = check_output(command) output = check_output(command)
added = 0 details.added = 0
modified = 0 details.modified = 0
deleted = 0 details.deleted = 0
for line in output.splitlines(): for line in output.splitlines():
status = line.split()[0].strip().decode('ascii') status = line.split()[0].strip().decode('ascii')
@ -37,18 +41,19 @@ def checkup_committed():
# added in the index but deleted on disk, etc. Anyway these numbers # added in the index but deleted on disk, etc. Anyway these numbers
# don't need to be super accurate, just enough to remind you to commit. # don't need to be super accurate, just enough to remind you to commit.
if {'A', '?'}.intersection(status): if {'A', '?'}.intersection(status):
added += 1 details.added += 1
if {'M', 'R', '!'}.intersection(status): if {'M', 'R', '!'}.intersection(status):
modified += 1 details.modified += 1
if {'D'}.intersection(status): if {'D'}.intersection(status):
deleted += 1 details.deleted += 1
committed = (added, modified, deleted) == (0, 0, 0) details.committed = (details.added, details.modified, details.deleted) == (0, 0, 0)
details = types.SimpleNamespace(added=added, deleted=deleted, modified=modified)
return (committed, details) return details
def checkup_pushed(): def checkup_pushed():
details = dotdict.DotDict(default=None)
command = [GIT, 'rev-parse', '@'] command = [GIT, 'rev-parse', '@']
my_head = check_output(command).strip().decode() my_head = check_output(command).strip().decode()
@ -58,33 +63,28 @@ def checkup_pushed():
except subprocess.CalledProcessError as exc: except subprocess.CalledProcessError as exc:
command = [GIT, 'rev-parse', '--abbrev-ref', 'HEAD'] command = [GIT, 'rev-parse', '--abbrev-ref', 'HEAD']
current_branch = check_output(command).strip().decode() current_branch = check_output(command).strip().decode()
raise NoUpstreamBranch(current_branch) details.error = NoUpstreamBranch(current_branch)
return details
if my_head == remote_head: if my_head == remote_head:
to_push = 0 details.to_push = 0
to_pull = 0 details.to_pull = 0
else: else:
command = [GIT, 'merge-base', '@', '@{u}'] command = [GIT, 'merge-base', '@', '@{u}']
merge_base = check_output(command) merge_base = check_output(command).strip().decode()
merge_base = merge_base.strip().decode()
if my_head == merge_base: if my_head == merge_base:
to_push = 0 details.to_push = 0
to_pull = len(git_commits_between(merge_base, remote_head)) details.to_pull = len(git_commits_between(merge_base, remote_head))
elif remote_head == merge_base: elif remote_head == merge_base:
to_push = len(git_commits_between(merge_base, my_head)) details.to_push = len(git_commits_between(merge_base, my_head))
to_pull = 0 details.to_pull = 0
else: else:
to_push = len(git_commits_between(merge_base, my_head)) details.to_push = len(git_commits_between(merge_base, my_head))
to_pull = len(git_commits_between(merge_base, remote_head)) details.to_pull = len(git_commits_between(merge_base, remote_head))
pushed = (to_push, to_pull) == (0, 0) details.pushed = (details.to_push, details.to_pull) == (0, 0)
details = types.SimpleNamespace( return details
to_push=to_push,
to_pull=to_pull,
)
return (pushed, details)
def git_commits_between(a, b): def git_commits_between(a, b):
command = [GIT, 'log', '--oneline', f'{a}..{b}'] command = [GIT, 'log', '--oneline', f'{a}..{b}']
@ -100,12 +100,10 @@ def checkup(directory, do_fetch=False):
os.chdir(directory) os.chdir(directory)
if do_fetch: if do_fetch:
git_fetch() git_fetch()
(committed, commit_details) = checkup_committed() commit_details = checkup_committed()
(pushed, push_details) = checkup_pushed() push_details = checkup_pushed()
return types.SimpleNamespace( return dotdict.DotDict(
committed=committed,
commit_details=commit_details, commit_details=commit_details,
pushed=pushed,
push_details=push_details, push_details=push_details,
) )
@ -121,7 +119,36 @@ def read_directories_file():
return directories return directories
def gitcheckup(do_fetch=False): def gitcheckup_one(directory, do_fetch=False):
try:
result = checkup(directory, do_fetch=do_fetch)
except subprocess.CalledProcessError as exc:
raise Exception(exc.output)
committed = 'C' if result.commit_details.committed else ' '
pushed = 'P' if result.push_details.pushed else ' '
details = []
commit_details = []
if result.commit_details.added: commit_details.append(f'+{result.commit_details.added}')
if result.commit_details.deleted: commit_details.append(f'-{result.commit_details.deleted}')
if result.commit_details.modified: commit_details.append(f'~{result.commit_details.modified}')
commit_details = ', '.join(commit_details)
if commit_details: details.append(f'({commit_details})')
push_details = []
if result.push_details.to_push: push_details.append(f'{result.push_details.to_push}')
if result.push_details.to_pull: push_details.append(f'{result.push_details.to_pull}')
if result.push_details.error: push_details.append(f'!{result.push_details.error}')
push_details = ', '.join(push_details)
if push_details: details.append(f'({push_details})')
details = ' '.join(details)
details = (' ' + details).rstrip()
print(f'[{committed}][{pushed}] {directory}{details}')
def gitcheckup_all(do_fetch=False):
try: try:
directories = read_directories_file() directories = read_directories_file()
except FileNotFoundError as exc: except FileNotFoundError as exc:
@ -129,35 +156,10 @@ def gitcheckup(do_fetch=False):
return 1 return 1
for directory in directories: for directory in directories:
try: gitcheckup_one(directory, do_fetch=do_fetch)
result = checkup(directory, do_fetch=do_fetch)
except subprocess.CalledProcessError as exc:
raise Exception(exc.output)
committed = 'C' if result.committed else ' '
pushed = 'P' if result.pushed else ' '
details = []
commit_details = []
if result.commit_details.added: commit_details.append(f'+{result.commit_details.added}')
if result.commit_details.deleted: commit_details.append(f'-{result.commit_details.deleted}')
if result.commit_details.modified: commit_details.append(f'~{result.commit_details.modified}')
commit_details = ', '.join(commit_details)
if commit_details: details.append(f'({commit_details})')
push_details = []
if result.push_details.to_push: push_details.append(f'{result.push_details.to_push}')
if result.push_details.to_pull: push_details.append(f'{result.push_details.to_pull}')
push_details = ', '.join(push_details)
if push_details: details.append(f'({push_details})')
details = ' '.join(details)
details = (' ' + details).rstrip()
print(f'[{committed}][{pushed}] {directory}{details}')
def gitcheckup_argparse(args): def gitcheckup_argparse(args):
return gitcheckup(do_fetch=args.do_fetch) return gitcheckup_all(do_fetch=args.do_fetch)
def main(argv): def main(argv):
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)