git_reparent_branch.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env python3
  2. # Copyright 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. """Change the upstream of the current branch."""
  6. import argparse
  7. import sys
  8. import subprocess2
  9. from git_common import upstream, current_branch, run, tags, set_branch_config
  10. from git_common import get_or_create_merge_base, root, manual_merge_base
  11. from git_common import get_branch_tree, topo_iter
  12. import gclient_utils
  13. import git_rebase_update
  14. import metrics
  15. @metrics.collector.collect_metrics('git reparent-branch')
  16. def main(args):
  17. if gclient_utils.IsEnvCog():
  18. print(
  19. 'reparent-branch command is not supported in non-git environment.',
  20. file=sys.stderr)
  21. return 1
  22. root_ref = root()
  23. parser = argparse.ArgumentParser()
  24. g = parser.add_mutually_exclusive_group()
  25. g.add_argument('new_parent',
  26. nargs='?',
  27. help='New parent branch (or tag) to reparent to.')
  28. g.add_argument('--root',
  29. action='store_true',
  30. help='Reparent to the configured root branch (%s).' %
  31. root_ref)
  32. g.add_argument('--lkgr',
  33. action='store_true',
  34. help='Reparent to the lkgr tag.')
  35. opts = parser.parse_args(args)
  36. # TODO(iannucci): Allow specification of the branch-to-reparent
  37. branch = current_branch()
  38. if opts.root:
  39. new_parent = root_ref
  40. elif opts.lkgr:
  41. new_parent = 'lkgr'
  42. else:
  43. if not opts.new_parent:
  44. parser.error('Must specify new parent somehow')
  45. new_parent = opts.new_parent
  46. cur_parent = upstream(branch)
  47. if branch == 'HEAD' or not branch:
  48. parser.error('Must be on the branch you want to reparent')
  49. if new_parent == cur_parent:
  50. parser.error('Cannot reparent a branch to its existing parent')
  51. if not cur_parent:
  52. msg = (
  53. "Unable to determine %s@{upstream}.\n\nThis can happen if you "
  54. "didn't use `git new-branch` to create the branch and haven't used "
  55. "`git branch --set-upstream-to` to assign it one.\n\nPlease assign "
  56. "an upstream branch and then run this command again.")
  57. print(msg % branch, file=sys.stderr)
  58. return 1
  59. mbase = get_or_create_merge_base(branch, cur_parent)
  60. all_tags = tags()
  61. if cur_parent in all_tags:
  62. cur_parent += ' [tag]'
  63. try:
  64. run('show-ref', new_parent)
  65. except subprocess2.CalledProcessError:
  66. print('fatal: invalid reference: %s' % new_parent, file=sys.stderr)
  67. return 1
  68. if new_parent in all_tags:
  69. print("Reparenting %s to track %s [tag] (was %s)" %
  70. (branch, new_parent, cur_parent))
  71. set_branch_config(branch, 'remote', '.')
  72. set_branch_config(branch, 'merge', new_parent)
  73. else:
  74. print("Reparenting %s to track %s (was %s)" %
  75. (branch, new_parent, cur_parent))
  76. run('branch', '--set-upstream-to', new_parent, branch)
  77. manual_merge_base(branch, mbase, new_parent)
  78. # ONLY rebase-update the branch which moved (and dependants)
  79. _, branch_tree = get_branch_tree()
  80. branches = [branch]
  81. for branch, parent in topo_iter(branch_tree):
  82. if parent in branches:
  83. branches.append(branch)
  84. return git_rebase_update.main(['--no-fetch', '--keep-empty'] + branches)
  85. if __name__ == '__main__': # pragma: no cover
  86. try:
  87. with metrics.collector.print_notice_and_exit():
  88. sys.exit(main(sys.argv[1:]))
  89. except KeyboardInterrupt:
  90. sys.stderr.write('interrupted\n')
  91. sys.exit(1)