comparison hgext/rebase.py @ 46632:9989a276712f

errors: use more specific errors in rebase extension Differential Revision: https://phab.mercurial-scm.org/D9914
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 29 Jan 2021 16:33:12 -0800
parents 24a32dea6955
children 7ed7b13fc00a
comparison
equal deleted inserted replaced
46631:230f73019e49 46632:9989a276712f
142 # Empty src or already obsoleted - Do not return a destination 142 # Empty src or already obsoleted - Do not return a destination
143 if not src or src in obsoleted: 143 if not src or src in obsoleted:
144 return smartset.baseset() 144 return smartset.baseset()
145 dests = destutil.orphanpossibledestination(repo, src) 145 dests = destutil.orphanpossibledestination(repo, src)
146 if len(dests) > 1: 146 if len(dests) > 1:
147 raise error.Abort( 147 raise error.StateError(
148 _(b"ambiguous automatic rebase: %r could end up on any of %r") 148 _(b"ambiguous automatic rebase: %r could end up on any of %r")
149 % (src, dests) 149 % (src, dests)
150 ) 150 )
151 # We have zero or one destination, so we can just return here. 151 # We have zero or one destination, so we can just return here.
152 return smartset.baseset(dests) 152 return smartset.baseset(dests)
422 422
423 (self.originalwd, self.destmap, self.state) = result 423 (self.originalwd, self.destmap, self.state) = result
424 if self.collapsef: 424 if self.collapsef:
425 dests = set(self.destmap.values()) 425 dests = set(self.destmap.values())
426 if len(dests) != 1: 426 if len(dests) != 1:
427 raise error.Abort( 427 raise error.InputError(
428 _(b'--collapse does not work with multiple destinations') 428 _(b'--collapse does not work with multiple destinations')
429 ) 429 )
430 destrev = next(iter(dests)) 430 destrev = next(iter(dests))
431 destancestors = self.repo.changelog.ancestors( 431 destancestors = self.repo.changelog.ancestors(
432 [destrev], inclusive=True 432 [destrev], inclusive=True
467 if self.collapsef: 467 if self.collapsef:
468 branches = set() 468 branches = set()
469 for rev in self.state: 469 for rev in self.state:
470 branches.add(repo[rev].branch()) 470 branches.add(repo[rev].branch())
471 if len(branches) > 1: 471 if len(branches) > 1:
472 raise error.Abort( 472 raise error.InputError(
473 _(b'cannot collapse multiple named branches') 473 _(b'cannot collapse multiple named branches')
474 ) 474 )
475 475
476 # Calculate self.obsoletenotrebased 476 # Calculate self.obsoletenotrebased
477 obsrevs = _filterobsoleterevs(self.repo, self.state) 477 obsrevs = _filterobsoleterevs(self.repo, self.state)
1091 elif action == b'stop': 1091 elif action == b'stop':
1092 rbsrt = rebaseruntime(repo, ui) 1092 rbsrt = rebaseruntime(repo, ui)
1093 with repo.wlock(), repo.lock(): 1093 with repo.wlock(), repo.lock():
1094 rbsrt.restorestatus() 1094 rbsrt.restorestatus()
1095 if rbsrt.collapsef: 1095 if rbsrt.collapsef:
1096 raise error.Abort(_(b"cannot stop in --collapse session")) 1096 raise error.StateError(_(b"cannot stop in --collapse session"))
1097 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) 1097 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1098 if not (rbsrt.keepf or allowunstable): 1098 if not (rbsrt.keepf or allowunstable):
1099 raise error.Abort( 1099 raise error.StateError(
1100 _( 1100 _(
1101 b"cannot remove original changesets with" 1101 b"cannot remove original changesets with"
1102 b" unrebased descendants" 1102 b" unrebased descendants"
1103 ), 1103 ),
1104 hint=_( 1104 hint=_(
1218 b"interactive history editing is supported by the " 1218 b"interactive history editing is supported by the "
1219 b"'histedit' extension (see \"%s\")" 1219 b"'histedit' extension (see \"%s\")"
1220 ) 1220 )
1221 % help 1221 % help
1222 ) 1222 )
1223 raise error.Abort(msg) 1223 raise error.InputError(msg)
1224 1224
1225 if rbsrt.collapsemsg and not rbsrt.collapsef: 1225 if rbsrt.collapsemsg and not rbsrt.collapsef:
1226 raise error.Abort(_(b'message can only be specified with collapse')) 1226 raise error.InputError(
1227 _(b'message can only be specified with collapse')
1228 )
1227 1229
1228 if action: 1230 if action:
1229 if rbsrt.collapsef: 1231 if rbsrt.collapsef:
1230 raise error.Abort( 1232 raise error.InputError(
1231 _(b'cannot use collapse with continue or abort') 1233 _(b'cannot use collapse with continue or abort')
1232 ) 1234 )
1233 if action == b'abort' and opts.get(b'tool', False): 1235 if action == b'abort' and opts.get(b'tool', False):
1234 ui.warn(_(b'tool option will be ignored\n')) 1236 ui.warn(_(b'tool option will be ignored\n'))
1235 if action == b'continue': 1237 if action == b'continue':
1292 cmdutil.checkunfinished(repo) 1294 cmdutil.checkunfinished(repo)
1293 if not inmemory: 1295 if not inmemory:
1294 cmdutil.bailifchanged(repo) 1296 cmdutil.bailifchanged(repo)
1295 1297
1296 if ui.configbool(b'commands', b'rebase.requiredest') and not destf: 1298 if ui.configbool(b'commands', b'rebase.requiredest') and not destf:
1297 raise error.Abort( 1299 raise error.InputError(
1298 _(b'you must specify a destination'), 1300 _(b'you must specify a destination'),
1299 hint=_(b'use: hg rebase -d REV'), 1301 hint=_(b'use: hg rebase -d REV'),
1300 ) 1302 )
1301 1303
1302 dest = None 1304 dest = None
1386 % (b'+'.join(bytes(repo[r]) for r in base), dest) 1388 % (b'+'.join(bytes(repo[r]) for r in base), dest)
1387 ) 1389 )
1388 return None 1390 return None
1389 1391
1390 if wdirrev in rebaseset: 1392 if wdirrev in rebaseset:
1391 raise error.Abort(_(b'cannot rebase the working copy')) 1393 raise error.InputError(_(b'cannot rebase the working copy'))
1392 rebasingwcp = repo[b'.'].rev() in rebaseset 1394 rebasingwcp = repo[b'.'].rev() in rebaseset
1393 ui.log( 1395 ui.log(
1394 b"rebase", 1396 b"rebase",
1395 b"rebasing working copy parent: %r\n", 1397 b"rebasing working copy parent: %r\n",
1396 rebasingwcp, 1398 rebasingwcp,
1424 if size == 1: 1426 if size == 1:
1425 destmap[r] = destset.first() 1427 destmap[r] = destset.first()
1426 elif size == 0: 1428 elif size == 0:
1427 ui.note(_(b'skipping %s - empty destination\n') % repo[r]) 1429 ui.note(_(b'skipping %s - empty destination\n') % repo[r])
1428 else: 1430 else:
1429 raise error.Abort( 1431 raise error.InputError(
1430 _(b'rebase destination for %s is not unique') % repo[r] 1432 _(b'rebase destination for %s is not unique') % repo[r]
1431 ) 1433 )
1432 1434
1433 if dest is not None: 1435 if dest is not None:
1434 # single-dest case: assign dest to each rev in rebaseset 1436 # single-dest case: assign dest to each rev in rebaseset
1457 parents.add(p.rev()) 1459 parents.add(p.rev())
1458 if not parents: 1460 if not parents:
1459 return nullrev 1461 return nullrev
1460 if len(parents) == 1: 1462 if len(parents) == 1:
1461 return parents.pop() 1463 return parents.pop()
1462 raise error.Abort( 1464 raise error.StateError(
1463 _( 1465 _(
1464 b'unable to collapse on top of %d, there is more ' 1466 b'unable to collapse on top of %d, there is more '
1465 b'than one external parent: %s' 1467 b'than one external parent: %s'
1466 ) 1468 )
1467 % (max(destancestors), b', '.join(b"%d" % p for p in sorted(parents))) 1469 % (max(destancestors), b', '.join(b"%d" % p for p in sorted(parents)))
1657 msg = _(b"this rebase will cause divergences from: %s") 1659 msg = _(b"this rebase will cause divergences from: %s")
1658 h = _( 1660 h = _(
1659 b"to force the rebase please set " 1661 b"to force the rebase please set "
1660 b"experimental.evolution.allowdivergence=True" 1662 b"experimental.evolution.allowdivergence=True"
1661 ) 1663 )
1662 raise error.Abort(msg % (b",".join(divhashes),), hint=h) 1664 raise error.StateError(msg % (b",".join(divhashes),), hint=h)
1663 1665
1664 1666
1665 def successorrevs(unfi, rev): 1667 def successorrevs(unfi, rev):
1666 """yield revision numbers for successors of rev""" 1668 """yield revision numbers for successors of rev"""
1667 assert unfi.filtername is None 1669 assert unfi.filtername is None
1760 # 1762 #
1761 # C # rebase -r C -d D 1763 # C # rebase -r C -d D
1762 # /| # None of A and B will be changed to D and rebase fails. 1764 # /| # None of A and B will be changed to D and rebase fails.
1763 # A B D 1765 # A B D
1764 if set(newps) == set(oldps) and dest not in newps: 1766 if set(newps) == set(oldps) and dest not in newps:
1765 raise error.Abort( 1767 raise error.InputError(
1766 _( 1768 _(
1767 b'cannot rebase %d:%s without ' 1769 b'cannot rebase %d:%s without '
1768 b'moving at least one of its parents' 1770 b'moving at least one of its parents'
1769 ) 1771 )
1770 % (rev, repo[rev]) 1772 % (rev, repo[rev])
1772 1774
1773 # Source should not be ancestor of dest. The check here guarantees it's 1775 # Source should not be ancestor of dest. The check here guarantees it's
1774 # impossible. With multi-dest, the initial check does not cover complex 1776 # impossible. With multi-dest, the initial check does not cover complex
1775 # cases since we don't have abstractions to dry-run rebase cheaply. 1777 # cases since we don't have abstractions to dry-run rebase cheaply.
1776 if any(p != nullrev and isancestor(rev, p) for p in newps): 1778 if any(p != nullrev and isancestor(rev, p) for p in newps):
1777 raise error.Abort(_(b'source is ancestor of destination')) 1779 raise error.InputError(_(b'source is ancestor of destination'))
1778 1780
1779 # Check if the merge will contain unwanted changes. That may happen if 1781 # Check if the merge will contain unwanted changes. That may happen if
1780 # there are multiple special (non-changelog ancestor) merge bases, which 1782 # there are multiple special (non-changelog ancestor) merge bases, which
1781 # cannot be handled well by the 3-way merge algorithm. For example: 1783 # cannot be handled well by the 3-way merge algorithm. For example:
1782 # 1784 #
1834 b', '.join(b'%d:%s' % (r, repo[r]) for r in revs) 1836 b', '.join(b'%d:%s' % (r, repo[r]) for r in revs)
1835 for revs in unwanted 1837 for revs in unwanted
1836 if revs is not None 1838 if revs is not None
1837 ) 1839 )
1838 ) 1840 )
1839 raise error.Abort( 1841 raise error.InputError(
1840 _(b'rebasing %d:%s will include unwanted changes from %s') 1842 _(b'rebasing %d:%s will include unwanted changes from %s')
1841 % (rev, repo[rev], unwanteddesc) 1843 % (rev, repo[rev], unwanteddesc)
1842 ) 1844 )
1843 1845
1844 # newps[0] should match merge base if possible. Currently, if newps[i] 1846 # newps[0] should match merge base if possible. Currently, if newps[i]
1979 result = [] 1981 result = []
1980 for r in srclist: 1982 for r in srclist:
1981 if destmap[r] not in srcset: 1983 if destmap[r] not in srcset:
1982 result.append(r) 1984 result.append(r)
1983 if not result: 1985 if not result:
1984 raise error.Abort(_(b'source and destination form a cycle')) 1986 raise error.InputError(_(b'source and destination form a cycle'))
1985 srcset -= set(result) 1987 srcset -= set(result)
1986 yield result 1988 yield result
1987 1989
1988 1990
1989 def buildstate(repo, destmap, collapse): 1991 def buildstate(repo, destmap, collapse):
1999 # applied patch. But it prevents messing up the working directory when 2001 # applied patch. But it prevents messing up the working directory when
2000 # a partially completed rebase is blocked by mq. 2002 # a partially completed rebase is blocked by mq.
2001 if b'qtip' in repo.tags(): 2003 if b'qtip' in repo.tags():
2002 mqapplied = {repo[s.node].rev() for s in repo.mq.applied} 2004 mqapplied = {repo[s.node].rev() for s in repo.mq.applied}
2003 if set(destmap.values()) & mqapplied: 2005 if set(destmap.values()) & mqapplied:
2004 raise error.Abort(_(b'cannot rebase onto an applied mq patch')) 2006 raise error.StateError(_(b'cannot rebase onto an applied mq patch'))
2005 2007
2006 # Get "cycle" error early by exhausting the generator. 2008 # Get "cycle" error early by exhausting the generator.
2007 sortedsrc = list(sortsource(destmap)) # a list of sorted revs 2009 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
2008 if not sortedsrc: 2010 if not sortedsrc:
2009 raise error.Abort(_(b'no matching revisions')) 2011 raise error.InputError(_(b'no matching revisions'))
2010 2012
2011 # Only check the first batch of revisions to rebase not depending on other 2013 # Only check the first batch of revisions to rebase not depending on other
2012 # rebaseset. This means "source is ancestor of destination" for the second 2014 # rebaseset. This means "source is ancestor of destination" for the second
2013 # (and following) batches of revisions are not checked here. We rely on 2015 # (and following) batches of revisions are not checked here. We rely on
2014 # "defineparents" to do that check. 2016 # "defineparents" to do that check.
2015 roots = list(repo.set(b'roots(%ld)', sortedsrc[0])) 2017 roots = list(repo.set(b'roots(%ld)', sortedsrc[0]))
2016 if not roots: 2018 if not roots:
2017 raise error.Abort(_(b'no matching revisions')) 2019 raise error.InputError(_(b'no matching revisions'))
2018 2020
2019 def revof(r): 2021 def revof(r):
2020 return r.rev() 2022 return r.rev()
2021 2023
2022 roots = sorted(roots, key=revof) 2024 roots = sorted(roots, key=revof)
2024 emptyrebase = len(sortedsrc) == 1 2026 emptyrebase = len(sortedsrc) == 1
2025 for root in roots: 2027 for root in roots:
2026 dest = repo[destmap[root.rev()]] 2028 dest = repo[destmap[root.rev()]]
2027 commonbase = root.ancestor(dest) 2029 commonbase = root.ancestor(dest)
2028 if commonbase == root: 2030 if commonbase == root:
2029 raise error.Abort(_(b'source is ancestor of destination')) 2031 raise error.InputError(_(b'source is ancestor of destination'))
2030 if commonbase == dest: 2032 if commonbase == dest:
2031 wctx = repo[None] 2033 wctx = repo[None]
2032 if dest == wctx.p1(): 2034 if dest == wctx.p1():
2033 # when rebasing to '.', it will use the current wd branch name 2035 # when rebasing to '.', it will use the current wd branch name
2034 samebranch = root.branch() == wctx.branch() 2036 samebranch = root.branch() == wctx.branch()
2117 """Call rebase after pull if the latter has been invoked with --rebase""" 2119 """Call rebase after pull if the latter has been invoked with --rebase"""
2118 if opts.get('rebase'): 2120 if opts.get('rebase'):
2119 if ui.configbool(b'commands', b'rebase.requiredest'): 2121 if ui.configbool(b'commands', b'rebase.requiredest'):
2120 msg = _(b'rebase destination required by configuration') 2122 msg = _(b'rebase destination required by configuration')
2121 hint = _(b'use hg pull followed by hg rebase -d DEST') 2123 hint = _(b'use hg pull followed by hg rebase -d DEST')
2122 raise error.Abort(msg, hint=hint) 2124 raise error.InputError(msg, hint=hint)
2123 2125
2124 with repo.wlock(), repo.lock(): 2126 with repo.wlock(), repo.lock():
2125 if opts.get('update'): 2127 if opts.get('update'):
2126 del opts['update'] 2128 del opts['update']
2127 ui.debug( 2129 ui.debug(
2174 # not passing argument to get the bare update behavior 2176 # not passing argument to get the bare update behavior
2175 # with warning and trumpets 2177 # with warning and trumpets
2176 commands.update(ui, repo) 2178 commands.update(ui, repo)
2177 else: 2179 else:
2178 if opts.get('tool'): 2180 if opts.get('tool'):
2179 raise error.Abort(_(b'--tool can only be used with --rebase')) 2181 raise error.InputError(_(b'--tool can only be used with --rebase'))
2180 ret = orig(ui, repo, *args, **opts) 2182 ret = orig(ui, repo, *args, **opts)
2181 2183
2182 return ret 2184 return ret
2183 2185
2184 2186