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