Created
December 1, 2016 20:53
-
-
Save jaredly/eea23db764ef081474c4e534d0438320 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # Toss this in your ~/bin/ as ~/bin/reboost and `chmod +x ~/bin/reboost` | |
| """\ | |
| Usage: | |
| reboost [cmd] | |
| Commands: | |
| first - follow upstream until just before master | |
| last - follow downstreams all the way | |
| up - go to parent | |
| down - go to child | |
| all - rebase & follow downstream, rebasing all the way | |
| """ | |
| from collections import defaultdict | |
| import subprocess | |
| import os | |
| import sys | |
| import re | |
| example = '''\ | |
| examples-7 c377c75 [gen-files-6: ahead 2] wrong call | |
| flow-ast-2 751352e [initial-diff-setup-1: ahead 1, behind 1] Add flowtype ast parsing for rn-codegen | |
| gen-files-6 7c56ae7 [marshall-code-5: ahead 1] Implement swift class generation | |
| * initial-diff-setup-1 29c611a [master: ahead 2] use error | |
| marshall-code-5 9fe3d2e [vc-props-4: ahead 1] Implement serialization and deserialization [ReactNative codegen] | |
| master 2ca33d5 [origin/master] empty readme | |
| saved e87821b [phab-prep: gone] docs | |
| signatures-3 2642f3f [flow-ast-2: ahead 1] Implement type signature generation [ReactNative codegen] | |
| vc-props-4 9f0b957 [signatures-3: ahead 1] Implement code gen for the ViewController's properties''' | |
| rx = '\s*(\*)?\s*(\S+)\s+(\S+)\s+(.+)' | |
| def parse_branches(txt): | |
| lines = txt.strip().split('\n') | |
| current = None | |
| children = defaultdict(list) | |
| parents = {} | |
| for line in lines: | |
| line = line.strip() | |
| match = re.match(rx, line) | |
| if not match: | |
| print ">> unexpected output line from git branch: %s" % line | |
| continue | |
| is_current, name, sha, extra = match.groups() | |
| if is_current is not None: | |
| current = name | |
| if extra[0] != '[': | |
| # no parent | |
| # TODO maybe keep track of commit message? | |
| continue | |
| parentage = extra[1:].split('] ', 1)[0] | |
| if ':' not in parentage: | |
| parent = parentage | |
| status = '' | |
| else: | |
| parent, status = parentage.split(': ', 1) | |
| children[parent].append(name) | |
| parents[name] = (parent, status) | |
| if not current: | |
| raise Exception('Current branch not found') | |
| return current, parents, children | |
| def get_branches(): | |
| return parse_branches(subprocess.check_output(['git', 'branch', '-vv'])) | |
| def get_children(): | |
| current, parents, children = get_branches() | |
| return children[current] | |
| def get_next_branch(): | |
| children = get_children() | |
| if not children: | |
| return # no children, reached the end | |
| if len(children) != 1: | |
| raise Exception("Current branch must have only one child") | |
| child = children[0] | |
| return child | |
| def rebase_children(): | |
| for i in range(20): | |
| child = get_next_branch() | |
| if not child: | |
| print "No more children" | |
| break | |
| print "Moving to", child | |
| subprocess.check_call(['git', 'checkout', child], stdout=sys.stdout, stderr=sys.stderr) | |
| subprocess.check_call(['git', 'rebase'], stdout=sys.stdout, stderr=sys.stderr) | |
| def go_to_first(): | |
| current, parents, _ = get_branches() | |
| while current in parents and parents[current][0] != 'master': | |
| current = parents[current][0] | |
| print "Checkout", current | |
| subprocess.check_call(['git', 'checkout', current]) | |
| def go_to_last(): | |
| current, _, children = get_branches() | |
| while len(children[current]) == 1: | |
| current = children[current][0] | |
| print "Checkout", current | |
| subprocess.check_call(['git', 'checkout', current]) | |
| def go_to_parent(): | |
| current, parents, _ = get_branches() | |
| if parents[current][0] != 'master': | |
| current = parents[current][0] | |
| print "Checkout", current | |
| subprocess.check_call(['git', 'checkout', current]) | |
| def go_to_child(): | |
| current, _, children = get_branches() | |
| if len(children[current]) == 1: | |
| current = children[current][0] | |
| print "Checkout", current | |
| subprocess.check_call(['git', 'checkout', current]) | |
| def show_pedigree(): | |
| current, parents, children = get_branches() | |
| branch = current | |
| while len(children[branch]) == 1: | |
| branch = children[branch][0] | |
| while branch in parents: | |
| print ('* ' if branch == current else ' ') + branch + ': ' + parents[branch][1] | |
| branch = parents[branch][0] | |
| print ('* ' if branch == current else ' ') + branch, '(root)' | |
| def print_help(): | |
| try: | |
| current, parents, children = get_branches() | |
| print "Status:" | |
| print " - current: ", current | |
| print " - parent: ", parents[current] | |
| print " - children: ", children[current] | |
| except: | |
| pass | |
| print __doc__ | |
| if len(sys.argv) > 1: | |
| cmd = sys.argv[1] | |
| if cmd == 'first': | |
| go_to_first() | |
| elif cmd == 'last': | |
| go_to_last() | |
| elif cmd == 'up': | |
| go_to_parent() | |
| elif cmd == 'down': | |
| go_to_child() | |
| elif cmd == 'all': | |
| rebase_children() | |
| elif cmd in ('l', 'list', 'll'): | |
| show_pedigree() | |
| else: | |
| print_help() | |
| else: | |
| print_help() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment