mercurial/merge.py
changeset 42456 87a34c767384
parent 42412 127937874395
child 42522 d29db0a0c4eb
equal deleted inserted replaced
42455:5ca136bbd3f6 42456:87a34c767384
     8 from __future__ import absolute_import
     8 from __future__ import absolute_import
     9 
     9 
    10 import errno
    10 import errno
    11 import hashlib
    11 import hashlib
    12 import shutil
    12 import shutil
       
    13 import stat
    13 import struct
    14 import struct
    14 
    15 
    15 from .i18n import _
    16 from .i18n import _
    16 from .node import (
    17 from .node import (
    17     addednodeid,
    18     addednodeid,
   681         return actions
   682         return actions
   682 
   683 
   683     def recordactions(self):
   684     def recordactions(self):
   684         """record remove/add/get actions in the dirstate"""
   685         """record remove/add/get actions in the dirstate"""
   685         branchmerge = self._repo.dirstate.p2() != nullid
   686         branchmerge = self._repo.dirstate.p2() != nullid
   686         recordupdates(self._repo, self.actions(), branchmerge)
   687         recordupdates(self._repo, self.actions(), branchmerge, None)
   687 
   688 
   688     def queueremove(self, f):
   689     def queueremove(self, f):
   689         """queues a file to be removed from the dirstate
   690         """queues a file to be removed from the dirstate
   690 
   691 
   691         Meant for use by custom merge drivers."""
   692         Meant for use by custom merge drivers."""
  1462         # cwd was removed in the course of removing files; print a helpful
  1463         # cwd was removed in the course of removing files; print a helpful
  1463         # warning.
  1464         # warning.
  1464         repo.ui.warn(_("current directory was removed\n"
  1465         repo.ui.warn(_("current directory was removed\n"
  1465                        "(consider changing to repo root: %s)\n") % repo.root)
  1466                        "(consider changing to repo root: %s)\n") % repo.root)
  1466 
  1467 
  1467 def batchget(repo, mctx, wctx, actions):
  1468 def batchget(repo, mctx, wctx, wantfiledata, actions):
  1468     """apply gets to the working directory
  1469     """apply gets to the working directory
  1469 
  1470 
  1470     mctx is the context to get from
  1471     mctx is the context to get from
  1471 
  1472 
  1472     yields tuples for progress updates
  1473     Yields arbitrarily many (False, tuple) for progress updates, followed by
       
  1474     exactly one (True, filedata). When wantfiledata is false, filedata is an
       
  1475     empty list. When wantfiledata is true, filedata[i] is a triple (mode, size,
       
  1476     mtime) of the file written for action[i].
  1473     """
  1477     """
       
  1478     filedata = []
  1474     verbose = repo.ui.verbose
  1479     verbose = repo.ui.verbose
  1475     fctx = mctx.filectx
  1480     fctx = mctx.filectx
  1476     ui = repo.ui
  1481     ui = repo.ui
  1477     i = 0
  1482     i = 0
  1478     with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
  1483     with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
  1492                             conflicting = p
  1497                             conflicting = p
  1493                             break
  1498                             break
  1494                 if repo.wvfs.lexists(conflicting):
  1499                 if repo.wvfs.lexists(conflicting):
  1495                     orig = scmutil.backuppath(ui, repo, conflicting)
  1500                     orig = scmutil.backuppath(ui, repo, conflicting)
  1496                     util.rename(repo.wjoin(conflicting), orig)
  1501                     util.rename(repo.wjoin(conflicting), orig)
  1497             wctx[f].clearunknown()
  1502             wfctx = wctx[f]
       
  1503             wfctx.clearunknown()
  1498             atomictemp = ui.configbool("experimental", "update.atomic-file")
  1504             atomictemp = ui.configbool("experimental", "update.atomic-file")
  1499             wctx[f].write(fctx(f).data(), flags, backgroundclose=True,
  1505             size = wfctx.write(fctx(f).data(), flags,
  1500                           atomictemp=atomictemp)
  1506                                backgroundclose=True,
       
  1507                                atomictemp=atomictemp)
       
  1508             if wantfiledata:
       
  1509                 s = wfctx.lstat()
       
  1510                 mode = s.st_mode
       
  1511                 mtime = s[stat.ST_MTIME]
       
  1512                 filedata.append((mode, size, mtime)) # for dirstate.normal
  1501             if i == 100:
  1513             if i == 100:
  1502                 yield i, f
  1514                 yield False, (i, f)
  1503                 i = 0
  1515                 i = 0
  1504             i += 1
  1516             i += 1
  1505     if i > 0:
  1517     if i > 0:
  1506         yield i, f
  1518         yield False, (i, f)
       
  1519     yield True, filedata
  1507 
  1520 
  1508 def _prefetchfiles(repo, ctx, actions):
  1521 def _prefetchfiles(repo, ctx, actions):
  1509     """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
  1522     """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
  1510     of merge actions.  ``ctx`` is the context being merged in."""
  1523     of merge actions.  ``ctx`` is the context being merged in."""
  1511 
  1524 
  1548                     ACTION_EXEC,
  1561                     ACTION_EXEC,
  1549                     ACTION_KEEP,
  1562                     ACTION_KEEP,
  1550                     ACTION_PATH_CONFLICT,
  1563                     ACTION_PATH_CONFLICT,
  1551                     ACTION_PATH_CONFLICT_RESOLVE))
  1564                     ACTION_PATH_CONFLICT_RESOLVE))
  1552 
  1565 
  1553 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
  1566 def applyupdates(repo, actions, wctx, mctx, overwrite, wantfiledata,
       
  1567                  labels=None):
  1554     """apply the merge action list to the working directory
  1568     """apply the merge action list to the working directory
  1555 
  1569 
  1556     wctx is the working copy context
  1570     wctx is the working copy context
  1557     mctx is the context to be merged into the working copy
  1571     mctx is the context to be merged into the working copy
  1558 
  1572 
  1559     Return a tuple of counts (updated, merged, removed, unresolved) that
  1573     Return a tuple of (counts, filedata), where counts is a tuple
  1560     describes how many files were affected by the update.
  1574     (updated, merged, removed, unresolved) that describes how many
       
  1575     files were affected by the update, and filedata is as described in
       
  1576     batchget.
  1561     """
  1577     """
  1562 
  1578 
  1563     _prefetchfiles(repo, mctx, actions)
  1579     _prefetchfiles(repo, mctx, actions)
  1564 
  1580 
  1565     updated, merged, removed = 0, 0, 0
  1581     updated, merged, removed = 0, 0, 0
  1647         progress.increment(item=f)
  1663         progress.increment(item=f)
  1648 
  1664 
  1649     # get in parallel.
  1665     # get in parallel.
  1650     threadsafe = repo.ui.configbool('experimental',
  1666     threadsafe = repo.ui.configbool('experimental',
  1651                                     'worker.wdir-get-thread-safe')
  1667                                     'worker.wdir-get-thread-safe')
  1652     prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx),
  1668     prog = worker.worker(repo.ui, cost, batchget,
       
  1669                          (repo, mctx, wctx, wantfiledata),
  1653                          actions[ACTION_GET],
  1670                          actions[ACTION_GET],
  1654                          threadsafe=threadsafe)
  1671                          threadsafe=threadsafe,
  1655     for i, item in prog:
  1672                          hasretval=True)
  1656         progress.increment(step=i, item=item)
  1673     getfiledata = []
       
  1674     for final, res in prog:
       
  1675         if final:
       
  1676             getfiledata = res
       
  1677         else:
       
  1678             i, item = res
       
  1679             progress.increment(step=i, item=item)
  1657     updated = len(actions[ACTION_GET])
  1680     updated = len(actions[ACTION_GET])
  1658 
  1681 
  1659     if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
  1682     if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
  1660         subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
  1683         subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
  1661 
  1684 
  1776     extraactions = ms.actions()
  1799     extraactions = ms.actions()
  1777     if extraactions:
  1800     if extraactions:
  1778         mfiles = set(a[0] for a in actions[ACTION_MERGE])
  1801         mfiles = set(a[0] for a in actions[ACTION_MERGE])
  1779         for k, acts in extraactions.iteritems():
  1802         for k, acts in extraactions.iteritems():
  1780             actions[k].extend(acts)
  1803             actions[k].extend(acts)
       
  1804             if k == ACTION_GET and wantfiledata:
       
  1805                 # no filedata until mergestate is updated to provide it
       
  1806                 getfiledata.extend([None] * len(acts))
  1781             # Remove these files from actions[ACTION_MERGE] as well. This is
  1807             # Remove these files from actions[ACTION_MERGE] as well. This is
  1782             # important because in recordupdates, files in actions[ACTION_MERGE]
  1808             # important because in recordupdates, files in actions[ACTION_MERGE]
  1783             # are processed after files in other actions, and the merge driver
  1809             # are processed after files in other actions, and the merge driver
  1784             # might add files to those actions via extraactions above. This can
  1810             # might add files to those actions via extraactions above. This can
  1785             # lead to a file being recorded twice, with poor results. This is
  1811             # lead to a file being recorded twice, with poor results. This is
  1798 
  1824 
  1799         actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE]
  1825         actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE]
  1800                                  if a[0] in mfiles]
  1826                                  if a[0] in mfiles]
  1801 
  1827 
  1802     progress.complete()
  1828     progress.complete()
  1803     return updateresult(updated, merged, removed, unresolved)
  1829     assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0)
  1804 
  1830     return updateresult(updated, merged, removed, unresolved), getfiledata
  1805 def recordupdates(repo, actions, branchmerge):
  1831 
       
  1832 def recordupdates(repo, actions, branchmerge, getfiledata):
  1806     "record merge actions to the dirstate"
  1833     "record merge actions to the dirstate"
  1807     # remove (must come first)
  1834     # remove (must come first)
  1808     for f, args, msg in actions.get(ACTION_REMOVE, []):
  1835     for f, args, msg in actions.get(ACTION_REMOVE, []):
  1809         if branchmerge:
  1836         if branchmerge:
  1810             repo.dirstate.remove(f)
  1837             repo.dirstate.remove(f)
  1844     # keep
  1871     # keep
  1845     for f, args, msg in actions.get(ACTION_KEEP, []):
  1872     for f, args, msg in actions.get(ACTION_KEEP, []):
  1846         pass
  1873         pass
  1847 
  1874 
  1848     # get
  1875     # get
  1849     for f, args, msg in actions.get(ACTION_GET, []):
  1876     for i, (f, args, msg) in enumerate(actions.get(ACTION_GET, [])):
  1850         if branchmerge:
  1877         if branchmerge:
  1851             repo.dirstate.otherparent(f)
  1878             repo.dirstate.otherparent(f)
  1852         else:
  1879         else:
  1853             repo.dirstate.normal(f)
  1880             parentfiledata = getfiledata[i] if getfiledata else None
       
  1881             repo.dirstate.normal(f, parentfiledata=parentfiledata)
  1854 
  1882 
  1855     # merge
  1883     # merge
  1856     for f, args, msg in actions.get(ACTION_MERGE, []):
  1884     for f, args, msg in actions.get(ACTION_MERGE, []):
  1857         f1, f2, fa, move, anc = args
  1885         f1, f2, fa, move, anc = args
  1858         if branchmerge:
  1886         if branchmerge:
  2164             repo.ui.warn(
  2192             repo.ui.warn(
  2165                 _('(warning: large working directory being used without '
  2193                 _('(warning: large working directory being used without '
  2166                   'fsmonitor enabled; enable fsmonitor to improve performance; '
  2194                   'fsmonitor enabled; enable fsmonitor to improve performance; '
  2167                   'see "hg help -e fsmonitor")\n'))
  2195                   'see "hg help -e fsmonitor")\n'))
  2168 
  2196 
  2169         stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
  2197         updatedirstate = not partial and not wc.isinmemory()
  2170 
  2198         wantfiledata = updatedirstate and not branchmerge
  2171         if not partial and not wc.isinmemory():
  2199         stats, getfiledata = applyupdates(repo, actions, wc, p2, overwrite,
       
  2200                                           wantfiledata, labels=labels)
       
  2201 
       
  2202         if updatedirstate:
  2172             with repo.dirstate.parentchange():
  2203             with repo.dirstate.parentchange():
  2173                 repo.setparents(fp1, fp2)
  2204                 repo.setparents(fp1, fp2)
  2174                 recordupdates(repo, actions, branchmerge)
  2205                 recordupdates(repo, actions, branchmerge, getfiledata)
  2175                 # update completed, clear state
  2206                 # update completed, clear state
  2176                 util.unlink(repo.vfs.join('updatestate'))
  2207                 util.unlink(repo.vfs.join('updatestate'))
  2177 
  2208 
  2178                 if not branchmerge:
  2209                 if not branchmerge:
  2179                     repo.dirstate.setbranch(p2.branch())
  2210                     repo.dirstate.setbranch(p2.branch())