diff --git a/gitcheckup.py b/gitcheckup.py index c05529b..704c405 100644 --- a/gitcheckup.py +++ b/gitcheckup.py @@ -1,3 +1,4 @@ +import argparse import os import subprocess import sys @@ -14,16 +15,7 @@ GIT = winwhich.which('git') # A file4 # ?? file3 -# Here is an example of typical `git log --oneline --branches --not --remotes` output: -# Only the commits that haven't been pushed to a remote are shown! -# Thank you cxreg https://stackoverflow.com/a/3338774 -# -# a755f32 Ready to publish -# 5602e1e Another commit message -# a32a372 My commit message - -def checkup_committed(directory): - os.chdir(directory) +def checkup_committed(): command = [GIT, 'status', '--short', '--untracked-files=all'] output = subprocess.check_output(command, stderr=subprocess.STDOUT) @@ -48,29 +40,64 @@ def checkup_committed(directory): details = '' else: committed = False - details = f'(+{added}, -{deleted}, ~{modified})' + details = [] + if added: details.append(f'+{added}') + if deleted: details.append(f'-{deleted}') + if modified: details.append(f'~{modified}') + details = ', '.join(details) + details = f'({details})' return (committed, details) -def checkup_pushed(directory): - os.chdir(directory) - command = [GIT, 'log', '--oneline', '--branches', '--not', '--remotes'] - output = subprocess.check_output(command, stderr=subprocess.STDOUT) +def checkup_pushed(): + command = [GIT, 'rev-parse', '@'] + my_head = subprocess.check_output(command, stderr=subprocess.STDOUT) + my_head = my_head.strip().decode() - commits = sum(1 for line in output.splitlines() if line.strip()) + command = [GIT, 'rev-parse', '@{u}'] + remote_head = subprocess.check_output(command, stderr=subprocess.STDOUT) + remote_head = remote_head.strip().decode() - if commits == 0: + command = [GIT, 'merge-base', '@', '@{u}'] + merge_base = subprocess.check_output(command, stderr=subprocess.STDOUT) + merge_base = merge_base.strip().decode() + + if my_head == remote_head: + to_push = 0 + to_pull = 0 + else: + to_push = len(git_commits_between(merge_base, my_head)) + to_pull = len(git_commits_between(merge_base, remote_head)) + + if (to_push, to_pull) == (0, 0): pushed = True details = '' else: pushed = False - details = f'(↑{commits})' + details = [] + if to_push: details.append(f'↑{to_push}') + if to_pull: details.append(f'↓{to_pull}') + details = ', '.join(details) + details = f'({details})' return (pushed, details) -def checkup(directory): - (committed, commit_details) = checkup_committed(directory) - (pushed, push_details) = checkup_pushed(directory) +def git_commits_between(a, b): + command = [GIT, 'log', '--oneline', f'{a}..{b}'] + output = subprocess.check_output(command, stderr=subprocess.STDOUT) + lines = output.strip().decode().splitlines() + return lines + +def git_fetch(): + command = [GIT, 'fetch', '--all'] + output = subprocess.check_output(command, stderr=subprocess.STDOUT) + +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 { 'committed': committed, 'commit_details': commit_details, @@ -78,7 +105,7 @@ def checkup(directory): 'push_details': push_details, } -def main(argv): +def gitcheckup(do_fetch=False): directories_file = os.path.join(os.path.dirname(__file__), 'gitcheckup.txt') try: handle = open(directories_file, 'r') @@ -93,7 +120,7 @@ def main(argv): directories = [line for line in directories if line] for directory in directories: - result = checkup(directory) + result = checkup(directory, do_fetch=do_fetch) committed = 'C' if result['committed'] else ' ' pushed = 'P' if result['pushed'] else ' ' @@ -106,5 +133,17 @@ def main(argv): details = (' ' + details).rstrip() print(f'[{committed}][{pushed}] {directory}{details}') +def gitcheckup_argparse(args): + return gitcheckup(do_fetch=args.do_fetch) + +def main(argv): + parser = argparse.ArgumentParser(description=__doc__) + + parser.add_argument('--fetch', dest='do_fetch', action='store_true') + parser.set_defaults(func=gitcheckup_argparse) + + args = parser.parse_args(argv) + return args.func(args) + if __name__ == '__main__': raise SystemExit(main(sys.argv[1:]))