From: Mike Frysinger Date: Mon, 15 Feb 2021 04:30:02 +0000 (-0500) Subject: bin: improve rebase helpers X-Git-Url: https://git.wh0rd.org/?a=commitdiff_plain;h=2ea871e750a0999351c3a9d3c43bf1bf62cf47f9;p=home.git bin: improve rebase helpers Rewrite rb-all in python to make it faster. Add a new "catchup" mode which tries to rebase onto the most recent commit as possible. --- diff --git a/.bin/git-rb-all b/.bin/git-rb-all index be05ba6..1d9ded3 100755 --- a/.bin/git-rb-all +++ b/.bin/git-rb-all @@ -1,63 +1,179 @@ -#!/bin/bash -# Helper to rewrite all local branches. - -rb_one() { - local b=$1 - shift - - printf "${BRACKET}### ${GREEN}${b}${NORMAL}" - if ! git config --local "branch.${b}.merge" >/dev/null; then - echo " -> skipping due to missing merge branch" - else - echo - git checkout -q "${b}" || return - git rebase "${opts[@]}" | sed -e '/^Fast-forwarded/d' -e "s:^:${BAD}:" -e "s:$:${NORMAL}:" - if [[ ${PIPESTATUS[0]} -ne 0 ]] ; then - git rebase --abort - fi - fi -} - -usage() { - cat <= max: + break + if attempt(mid): + max = mid + else: + min = mid + old_mid = mid + print('Done') + + +def get_ahead_behind(lbranch, rbranch): + """Return number of commits |lbranch| is ahead & behind relative to |rbranch|.""" + output = git(['rev-list', '--left-right', '--count', f'{lbranch}...{rbranch}']).stdout + return [int(x) for x in output.split()] + + +def get_tracking_branch(branch): + """Return remote branch that |branch| is tracking.""" + merge = git(['config', '--local', f'branch.{branch}.merge']).stdout.strip() + if not merge: + return None + + remote = git(['config', '--local', f'branch.{branch}.remote']).stdout.strip() + if remote: + if merge.startswith('refs/heads/'): + merge = merge[11:] + return f'{remote}/{merge}' + else: + return merge + + +def get_local_branch(): + """Return the name of the local checked out branch.""" + return git(['branch', '--show-current']).stdout.strip() + + +def get_parser(): + """Get CLI parser.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--skip-initial-rebase-latest', dest='initial_rebase', + action='store_false', default=True, + help='skip initial rebase attempt onto the latest branch') + parser.add_argument( + 'branch', nargs='?', + help='branch to rebase onto') + return parser + + +def main(argv): + """The main entry point for scripts.""" + parser = get_parser() + opts = parser.parse_args(argv) + + lbranch = get_local_branch() + print(f'Local branch resolved to "{lbranch}"') + if not lbranch: + print('Unable to resolve local branch', file=sys.stderr) + return 1 + + if opts.branch: + rbranch = opts.branch + else: + rbranch = get_tracking_branch(lbranch) + print(f'Remote branch resolved to "{rbranch}"') + + ahead, behind = get_ahead_behind(lbranch, rbranch) + print(f'Branch is {ahead} commits ahead and {behind} commits behind') + + if not behind: + print('Up-to-date!') + elif not ahead: + print('Fast forwarding ...') + git(['merge']) + else: + if opts.initial_rebase: + print(f'Trying to rebase onto latest {rbranch} ... ', end='', flush=True) + if rebase(rbranch): + print('OK!') + return 0 + print('failed; falling back to bisect') + rebase_bisect(lbranch, rbranch, behind) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))