]>
git.wh0rd.org - home.git/blob - .bin/git-rb-catchup
3 """Helper to automatically rebase onto latest commit possible."""
10 def git(args
, **kwargs
):
12 kwargs
.setdefault('check', True)
13 kwargs
.setdefault('capture_output', True)
14 kwargs
.setdefault('encoding', 'utf-8')
15 return subprocess
.run(['git'] + args
, **kwargs
)
19 """Try to rebase onto |target|."""
21 git(['rebase', target
])
23 except KeyboardInterrupt:
24 git(['rebase', '--abort'])
28 git(['rebase', '--abort'])
32 def rebase_bisect(lbranch
, rbranch
, behind
, leave_rebase
=False):
33 """Try to rebase branch as close to |rbranch| as possible."""
35 target
= f
'{rbranch}~{pos}'
36 print(f
'Rebasing onto {target} ', end
='', flush
=True)
37 print('.', end
='', flush
=True)
38 # git(['checkout', '-f', target])
39 print('.', end
='', flush
=True)
40 # git(['checkout', '-f', lbranch])
41 print('. ', end
='', flush
=True)
43 print('OK' if ret
else 'failed')
46 # "pmin" is the latest branch position while "pmax" is where we're now.
52 mid
= pmin
+ (pmax
- pmin
) // 2
53 if mid
== old_mid
or mid
< pmin
or mid
>= pmax
:
58 first_fail
= max(first_fail
, mid
)
63 last_target
= f
'{rbranch}~{first_fail}'
65 print('Restarting', last_target
)
66 result
= git(['rebase', last_target
], check
=False)
67 print(result
.stdout
.strip())
69 print('Found first failure', last_target
)
71 print('All caught up!')
74 def get_ahead_behind(lbranch
, rbranch
):
75 """Return number of commits |lbranch| is ahead & behind relative to |rbranch|."""
76 output
= git(['rev-list', '--left-right', '--count', f
'{lbranch}...{rbranch}']).stdout
77 return [int(x
) for x
in output
.split()]
80 def get_tracking_branch(branch
):
81 """Return remote branch that |branch| is tracking."""
82 merge
= git(['config', '--local', f
'branch.{branch}.merge']).stdout
.strip()
86 remote
= git(['config', '--local', f
'branch.{branch}.remote']).stdout
.strip()
88 if merge
.startswith('refs/heads/'):
90 return f
'{remote}/{merge}'
95 def get_local_branch():
96 """Return the name of the local checked out branch."""
97 return git(['branch', '--show-current']).stdout
.strip()
101 """Get CLI parser."""
102 parser
= argparse
.ArgumentParser(description
=__doc__
)
104 '--skip-initial-rebase-latest', dest
='initial_rebase',
105 action
='store_false', default
=True,
106 help='skip initial rebase attempt onto the latest branch')
108 '--leave-at-last-failed-rebase', dest
='leave_rebase',
109 action
='store_true', default
=False,
110 help='leave tree state at last failing rebase')
113 help='branch to rebase onto')
118 """The main entry point for scripts."""
119 parser
= get_parser()
120 opts
= parser
.parse_args(argv
)
122 lbranch
= get_local_branch()
123 print(f
'Local branch resolved to "{lbranch}"')
125 print('Unable to resolve local branch', file=sys
.stderr
)
129 rbranch
= opts
.branch
131 rbranch
= get_tracking_branch(lbranch
)
132 print(f
'Remote branch resolved to "{rbranch}"')
134 ahead
, behind
= get_ahead_behind(lbranch
, rbranch
)
135 print(f
'Branch is {ahead} commits ahead and {behind} commits behind')
140 print('Fast forwarding ...')
143 if opts
.initial_rebase
:
144 print(f
'Trying to rebase onto latest {rbranch} ... ', end
='', flush
=True)
148 print('failed; falling back to bisect')
149 rebase_bisect(lbranch
, rbranch
, behind
, leave_rebase
=opts
.leave_rebase
)
152 if __name__
== '__main__':
153 sys
.exit(main(sys
.argv
[1:]))