comparison mercurial/merge.py @ 37107:71543b942eea

merge: return an attrs class from update() and applyupdates() Previously, we returned a tuple containing counts. The result of an update is kind of complex and the use of tuples with nameless fields made the code a bit harder to read and constrained future expansion of the return value. Let's invent an attrs-defined class for representing the result of an update operation. We provide __getitem__ and __len__ implementations for backwards compatibility as a container type to minimize code churn. In (at least) Python 2, the % operator seems to insist on using tuples. So we had to update a consumer using the % operator. .. api:: merge.update() and merge.applyupdates() now return a class with named attributes instead of a tuple. Switch consumers to access elements by name instead of by offset. Differential Revision: https://phab.mercurial-scm.org/D2692
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 05 Mar 2018 00:02:13 -0500
parents e4640ec346ac
children a532b2f54f95
comparison
equal deleted inserted replaced
37106:3d3cff1f6bde 37107:71543b942eea
19 hex, 19 hex,
20 modifiednodeid, 20 modifiednodeid,
21 nullhex, 21 nullhex,
22 nullid, 22 nullid,
23 nullrev, 23 nullrev,
24 )
25 from .thirdparty import (
26 attr,
24 ) 27 )
25 from . import ( 28 from . import (
26 copies, 29 copies,
27 error, 30 error,
28 filemerge, 31 filemerge,
1396 # changed/deleted never resolves to something from the remote side. 1399 # changed/deleted never resolves to something from the remote side.
1397 oplist = [actions[a] for a in 'g dc dg m'.split()] 1400 oplist = [actions[a] for a in 'g dc dg m'.split()]
1398 prefetch = scmutil.fileprefetchhooks 1401 prefetch = scmutil.fileprefetchhooks
1399 prefetch(repo, ctx, [f for sublist in oplist for f, args, msg in sublist]) 1402 prefetch(repo, ctx, [f for sublist in oplist for f, args, msg in sublist])
1400 1403
1404 @attr.s(frozen=True)
1405 class updateresult(object):
1406 updatedcount = attr.ib()
1407 mergedcount = attr.ib()
1408 removedcount = attr.ib()
1409 unresolvedcount = attr.ib()
1410
1411 # TODO remove container emulation once consumers switch to new API.
1412
1413 def __getitem__(self, x):
1414 if x == 0:
1415 return self.updatedcount
1416 elif x == 1:
1417 return self.mergedcount
1418 elif x == 2:
1419 return self.removedcount
1420 elif x == 3:
1421 return self.unresolvedcount
1422 else:
1423 raise IndexError('can only access items 0-3')
1424
1425 def __len__(self):
1426 return 4
1427
1401 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None): 1428 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1402 """apply the merge action list to the working directory 1429 """apply the merge action list to the working directory
1403 1430
1404 wctx is the working copy context 1431 wctx is the working copy context
1405 mctx is the context to be merged into the working copy 1432 mctx is the context to be merged into the working copy
1579 # the driver might leave some files unresolved 1606 # the driver might leave some files unresolved
1580 unresolvedf = set(ms.unresolved()) 1607 unresolvedf = set(ms.unresolved())
1581 if not proceed: 1608 if not proceed:
1582 # XXX setting unresolved to at least 1 is a hack to make sure we 1609 # XXX setting unresolved to at least 1 is a hack to make sure we
1583 # error out 1610 # error out
1584 return updated, merged, removed, max(len(unresolvedf), 1) 1611 return updateresult(updated, merged, removed,
1612 max(len(unresolvedf), 1))
1585 newactions = [] 1613 newactions = []
1586 for f, args, msg in mergeactions: 1614 for f, args, msg in mergeactions:
1587 if f in unresolvedf: 1615 if f in unresolvedf:
1588 newactions.append((f, args, msg)) 1616 newactions.append((f, args, msg))
1589 mergeactions = newactions 1617 mergeactions = newactions
1654 mfiles.difference_update(a[0] for a in acts) 1682 mfiles.difference_update(a[0] for a in acts)
1655 1683
1656 actions['m'] = [a for a in actions['m'] if a[0] in mfiles] 1684 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1657 1685
1658 progress(_updating, None, total=numupdates, unit=_files) 1686 progress(_updating, None, total=numupdates, unit=_files)
1659 1687 return updateresult(updated, merged, removed, unresolved)
1660 return updated, merged, removed, unresolved
1661 1688
1662 def recordupdates(repo, actions, branchmerge): 1689 def recordupdates(repo, actions, branchmerge):
1663 "record merge actions to the dirstate" 1690 "record merge actions to the dirstate"
1664 # remove (must come first) 1691 # remove (must come first)
1665 for f, args, msg in actions.get('r', []): 1692 for f, args, msg in actions.get('r', []):
1876 elif not overwrite: 1903 elif not overwrite:
1877 if p1 == p2: # no-op update 1904 if p1 == p2: # no-op update
1878 # call the hooks and exit early 1905 # call the hooks and exit early
1879 repo.hook('preupdate', throw=True, parent1=xp2, parent2='') 1906 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1880 repo.hook('update', parent1=xp2, parent2='', error=0) 1907 repo.hook('update', parent1=xp2, parent2='', error=0)
1881 return 0, 0, 0, 0 1908 return updateresult(0, 0, 0, 0)
1882 1909
1883 if (updatecheck == 'linear' and 1910 if (updatecheck == 'linear' and
1884 pas not in ([p1], [p2])): # nonlinear 1911 pas not in ([p1], [p2])): # nonlinear
1885 dirty = wc.dirty(missing=True) 1912 dirty = wc.dirty(missing=True)
1886 if dirty: 1913 if dirty: