Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 import logging | |
|
Ryan Tseng
2014/02/28 21:22:05
2 lines before line 5 + sort
| |
| 6 import sys | |
| 7 import collections | |
| 8 | |
| 9 from pprint import pformat | |
| 10 | |
| 11 from subprocess2 import CalledProcessError | |
| 12 | |
| 13 from git_squash_branch import squash | |
| 14 | |
|
agable
2014/02/28 20:14:16
No newline here, and put git_squash_branch bellow
| |
| 15 from git_common import run, root, upstream | |
| 16 from git_common import branches, current_branch, get_or_create_merge_base_tag | |
| 17 from git_common import clean_refs, hash_one | |
| 18 | |
| 19 RebaseRet = collections.namedtuple('TryRebaseRet', 'success message') | |
| 20 | |
| 21 | |
| 22 def bclean(): | |
|
Ryan Tseng
2014/02/28 21:22:05
What does this stand for?
| |
| 23 merged = list(branches('--merged', root())) | |
| 24 | |
| 25 logging.debug('merged: %s', merged) | |
| 26 | |
| 27 upstreams = {} | |
| 28 downstreams = collections.defaultdict(list) | |
| 29 for branch in branches(): | |
| 30 try: | |
| 31 parent = upstream(branch) | |
| 32 upstreams[branch] = parent | |
| 33 downstreams[parent].append(branch) | |
| 34 except CalledProcessError: | |
| 35 pass | |
| 36 | |
| 37 logging.debug('upstreams: %s', upstreams) | |
| 38 logging.debug('downstreams: %s', downstreams) | |
| 39 | |
| 40 if current_branch() in merged: | |
| 41 run('checkout', root()) | |
| 42 for branch in merged: | |
| 43 for down in downstreams[branch]: | |
| 44 if down not in merged: | |
| 45 run('branch', '--set-upstream-to', upstreams[branch], down) | |
| 46 print ('Reparented %s to track %s (was tracking %s)' | |
| 47 % (down, upstreams[branch], branch)) | |
| 48 print run('branch', '-d', branch) | |
| 49 | |
| 50 return 0 | |
| 51 | |
| 52 | |
| 53 def rebase(parent, start, branch, abort=False): | |
| 54 try: | |
| 55 run('rebase', '--onto', parent, start, branch) | |
| 56 return RebaseRet(True, '') | |
| 57 except CalledProcessError as cpe: | |
| 58 if abort: | |
| 59 run('rebase', '--abort') | |
| 60 return RebaseRet(False, cpe.output) | |
| 61 | |
| 62 | |
| 63 def clean_branch(branch, parent, starting_ref): | |
| 64 if (hash_one(parent) != hash_one(starting_ref) | |
|
agable
2014/02/28 20:14:16
Main line of code should be at top level. Reverse
| |
| 65 and hash_one(branch) != hash_one(starting_ref)): | |
| 66 print 'Rebasing:', branch | |
| 67 | |
| 68 # Try a plain rebase first | |
| 69 if rebase(parent, starting_ref, branch, abort=True).success: | |
| 70 return | |
| 71 | |
| 72 # Maybe squashing will help? | |
| 73 print "Failed! Attempting to squash", branch, "...", | |
| 74 squash_branch = branch+"_squash_attempt" | |
| 75 run('checkout', '-b', squash_branch) | |
| 76 squash() | |
| 77 | |
| 78 squash_ret = rebase(parent, starting_ref, squash_branch, abort=True) | |
| 79 run('checkout', branch) | |
| 80 run('branch', '-D', squash_branch) | |
| 81 | |
| 82 if squash_ret.success: | |
| 83 print 'Success!' | |
| 84 squash() | |
| 85 final_rebase = rebase(parent, starting_ref, branch) | |
| 86 assert final_rebase.success == squash_ret.success | |
|
agable
2014/02/28 20:14:16
Add error message here.
| |
| 87 | |
| 88 if not squash_ret.success: | |
| 89 print squash_ret.message | |
| 90 print 'Failure :(' | |
| 91 print 'Your working copy is in mid-rebase. Please completely resolve and' | |
|
Ryan Tseng
2014/02/28 21:22:05
Does this leave HEAD mid rebase?
I think we'll nee
iannucci
2014/03/20 10:59:37
It also prints the standard git rebase boilerplate
| |
| 92 print 'run `git reup` again.' | |
| 93 sys.exit(1) | |
| 94 | |
| 95 | |
| 96 def main(): | |
| 97 # TODO(iannucci): use argparse | |
|
agable
2014/02/28 20:14:16
Probably do this now (less important than the othe
Ryan Tseng
2014/02/28 21:22:05
We at least need a usage string behind --help. "r
| |
| 98 # TODO(iannucci): have a way to set log level | |
| 99 if '--clean' in sys.argv: | |
| 100 clean_refs() | |
| 101 return 0 | |
| 102 | |
| 103 # TODO(iannucci): check for a clean working dir | |
| 104 # TODO(iannucci): Tag merge bases more intelligently | |
| 105 # TODO(iannucci): Find collapsible branches in a smarter way | |
| 106 | |
| 107 # TODO(iannucci): Allow for root() to be a tag | |
| 108 # * update all remotes + tags | |
| 109 # * rebase to tag instead of to branch | |
| 110 # * when creating a new branch, use the tag as the ref | |
| 111 # * still need 'upstream' to be origin/master? | |
| 112 # * configurable? | |
| 113 | |
| 114 orig_branch = current_branch() | |
| 115 if orig_branch == 'HEAD': | |
| 116 orig_branch = hash_one('HEAD') | |
| 117 | |
| 118 if 'origin' in run('remote').splitlines(): | |
|
Ryan Tseng
2014/02/28 21:22:05
splitlines() is probably unnecessary.
| |
| 119 run('fetch', 'origin', stderr=None) | |
|
Ryan Tseng
2014/02/28 21:22:05
This might take a while, can we stream the output?
| |
| 120 else: | |
| 121 run('svn', 'fetch', stderr=None) | |
| 122 branch_tree = {} | |
| 123 for branch in branches(): | |
| 124 parent = upstream(branch) | |
| 125 if not parent: | |
| 126 print 'Skipping %s: No upstream specified' % branch | |
| 127 continue | |
| 128 branch_tree[branch] = parent | |
| 129 | |
| 130 starting_refs = {} | |
| 131 for branch, parent in branch_tree.iteritems(): | |
| 132 starting_refs[branch] = get_or_create_merge_base_tag(branch, parent) | |
| 133 | |
| 134 logging.debug('branch_tree: %s', pformat(branch_tree)) | |
| 135 logging.debug('starting_refs: %s', pformat(starting_refs)) | |
| 136 | |
| 137 # XXX There is a more efficient way to do this, but for now... | |
|
Ryan Tseng
2014/02/28 21:22:05
XXX?
| |
| 138 while branch_tree: | |
| 139 this_pass = [i for i in branch_tree.items() if i[1] not in branch_tree] | |
| 140 for branch, parent in this_pass: | |
| 141 clean_branch(branch, parent, starting_refs[branch]) | |
| 142 del branch_tree[branch] | |
| 143 | |
| 144 clean_refs() | |
| 145 | |
| 146 bclean() | |
| 147 | |
| 148 if orig_branch in branches(): | |
| 149 run('checkout', orig_branch) | |
| 150 else: | |
| 151 run('checkout', root()) | |
| 152 | |
| 153 return 0 | |
| 154 | |
| 155 | |
| 156 if __name__ == '__main__': | |
| 157 sys.exit(main()) | |
| OLD | NEW |