Show More
@@ -515,7 +515,7 b' def overridecalculateupdates(origfn, rep' | |||
|
515 | 515 | return actions, diverge, renamedelete |
|
516 | 516 | |
|
517 | 517 | @eh.wrapfunction(merge, 'recordupdates') |
|
518 | def mergerecordupdates(orig, repo, actions, branchmerge): | |
|
518 | def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata): | |
|
519 | 519 | if 'lfmr' in actions: |
|
520 | 520 | lfdirstate = lfutil.openlfdirstate(repo.ui, repo) |
|
521 | 521 | for lfile, args, msg in actions['lfmr']: |
@@ -526,7 +526,7 b' def mergerecordupdates(orig, repo, actio' | |||
|
526 | 526 | lfdirstate.add(lfile) |
|
527 | 527 | lfdirstate.write() |
|
528 | 528 | |
|
529 | return orig(repo, actions, branchmerge) | |
|
529 | return orig(repo, actions, branchmerge, getfiledata) | |
|
530 | 530 | |
|
531 | 531 | # Override filemerge to prompt the user about how they wish to merge |
|
532 | 532 | # largefiles. This will handle identical edits without prompting the user. |
@@ -16,21 +16,21 b' def wrapdirstate(repo, dirstate):' | |||
|
16 | 16 | """Add narrow spec dirstate ignore, block changes outside narrow spec.""" |
|
17 | 17 | |
|
18 | 18 | def _editfunc(fn): |
|
19 | def _wrapper(self, *args): | |
|
19 | def _wrapper(self, *args, **kwargs): | |
|
20 | 20 | narrowmatch = repo.narrowmatch() |
|
21 | 21 | for f in args: |
|
22 | 22 | if f is not None and not narrowmatch(f) and f not in self: |
|
23 | 23 | raise error.Abort(_("cannot track '%s' - it is outside " + |
|
24 | 24 | "the narrow clone") % f) |
|
25 | return fn(self, *args) | |
|
25 | return fn(self, *args, **kwargs) | |
|
26 | 26 | return _wrapper |
|
27 | 27 | |
|
28 | 28 | class narrowdirstate(dirstate.__class__): |
|
29 | 29 | # Prevent adding/editing/copying/deleting files that are outside the |
|
30 | 30 | # sparse checkout |
|
31 | 31 | @_editfunc |
|
32 | def normal(self, *args): | |
|
33 | return super(narrowdirstate, self).normal(*args) | |
|
32 | def normal(self, *args, **kwargs): | |
|
33 | return super(narrowdirstate, self).normal(*args, **kwargs) | |
|
34 | 34 | |
|
35 | 35 | @_editfunc |
|
36 | 36 | def add(self, *args): |
@@ -442,7 +442,8 b' def storewrapper(orig, requirements, pat' | |||
|
442 | 442 | return s |
|
443 | 443 | |
|
444 | 444 | # prefetch files before update |
|
445 |
def applyupdates(orig, repo, actions, wctx, mctx, overwrite, |
|
|
445 | def applyupdates(orig, repo, actions, wctx, mctx, overwrite, wantfiledata, | |
|
446 | labels=None): | |
|
446 | 447 | if isenabled(repo): |
|
447 | 448 | manifest = mctx.manifest() |
|
448 | 449 | files = [] |
@@ -450,7 +451,8 b' def applyupdates(orig, repo, actions, wc' | |||
|
450 | 451 | files.append((f, hex(manifest[f]))) |
|
451 | 452 | # batch fetch the needed files from the server |
|
452 | 453 | repo.fileservice.prefetch(files) |
|
453 |
return orig(repo, actions, wctx, mctx, overwrite, |
|
|
454 | return orig(repo, actions, wctx, mctx, overwrite, wantfiledata, | |
|
455 | labels=labels) | |
|
454 | 456 | |
|
455 | 457 | # Prefetch merge checkunknownfiles |
|
456 | 458 | def checkunknownfiles(orig, repo, wctx, mctx, force, actions, |
@@ -228,7 +228,7 b' def _setupdirstate(ui):' | |||
|
228 | 228 | hint = _('include file with `hg debugsparse --include <pattern>` or use ' + |
|
229 | 229 | '`hg add -s <file>` to include file directory while adding') |
|
230 | 230 | for func in editfuncs: |
|
231 | def _wrapper(orig, self, *args): | |
|
231 | def _wrapper(orig, self, *args, **kwargs): | |
|
232 | 232 | sparsematch = self._sparsematcher |
|
233 | 233 | if not sparsematch.always(): |
|
234 | 234 | for f in args: |
@@ -237,7 +237,7 b' def _setupdirstate(ui):' | |||
|
237 | 237 | raise error.Abort(_("cannot add '%s' - it is outside " |
|
238 | 238 | "the sparse checkout") % f, |
|
239 | 239 | hint=hint) |
|
240 | return orig(self, *args) | |
|
240 | return orig(self, *args, **kwargs) | |
|
241 | 241 | extensions.wrapfunction(dirstate.dirstate, func, _wrapper) |
|
242 | 242 | |
|
243 | 243 | @command('debugsparse', [ |
@@ -1766,6 +1766,8 b' class workingfilectx(committablefilectx)' | |||
|
1766 | 1766 | |
|
1767 | 1767 | def size(self): |
|
1768 | 1768 | return self._repo.wvfs.lstat(self._path).st_size |
|
1769 | def lstat(self): | |
|
1770 | return self._repo.wvfs.lstat(self._path) | |
|
1769 | 1771 | def date(self): |
|
1770 | 1772 | t, tz = self._changectx.date() |
|
1771 | 1773 | try: |
@@ -1801,9 +1803,9 b' class workingfilectx(committablefilectx)' | |||
|
1801 | 1803 | |
|
1802 | 1804 | def write(self, data, flags, backgroundclose=False, **kwargs): |
|
1803 | 1805 | """wraps repo.wwrite""" |
|
1804 | self._repo.wwrite(self._path, data, flags, | |
|
1805 | backgroundclose=backgroundclose, | |
|
1806 | **kwargs) | |
|
1806 | return self._repo.wwrite(self._path, data, flags, | |
|
1807 | backgroundclose=backgroundclose, | |
|
1808 | **kwargs) | |
|
1807 | 1809 | |
|
1808 | 1810 | def markcopied(self, src): |
|
1809 | 1811 | """marks this file a copy of `src`""" |
@@ -391,12 +391,24 b' class dirstate(object):' | |||
|
391 | 391 | self._updatedfiles.add(f) |
|
392 | 392 | self._map.addfile(f, oldstate, state, mode, size, mtime) |
|
393 | 393 | |
|
394 | def normal(self, f): | |
|
395 |
'''Mark a file normal and clean. |
|
|
396 | s = os.lstat(self._join(f)) | |
|
397 | mtime = s[stat.ST_MTIME] | |
|
398 | self._addpath(f, 'n', s.st_mode, | |
|
399 | s.st_size & _rangemask, mtime & _rangemask) | |
|
394 | def normal(self, f, parentfiledata=None): | |
|
395 | '''Mark a file normal and clean. | |
|
396 | ||
|
397 | parentfiledata: (mode, size, mtime) of the clean file | |
|
398 | ||
|
399 | parentfiledata should be computed from memory (for mode, | |
|
400 | size), as or close as possible from the point where we | |
|
401 | determined the file was clean, to limit the risk of the | |
|
402 | file having been changed by an external process between the | |
|
403 | moment where the file was determined to be clean and now.''' | |
|
404 | if parentfiledata: | |
|
405 | (mode, size, mtime) = parentfiledata | |
|
406 | else: | |
|
407 | s = os.lstat(self._join(f)) | |
|
408 | mode = s.st_mode | |
|
409 | size = s.st_size | |
|
410 | mtime = s[stat.ST_MTIME] | |
|
411 | self._addpath(f, 'n', mode, size & _rangemask, mtime & _rangemask) | |
|
400 | 412 | self._map.copymap.pop(f, None) |
|
401 | 413 | if f in self._map.nonnormalset: |
|
402 | 414 | self._map.nonnormalset.remove(f) |
@@ -10,6 +10,7 b' from __future__ import absolute_import' | |||
|
10 | 10 | import errno |
|
11 | 11 | import hashlib |
|
12 | 12 | import shutil |
|
13 | import stat | |
|
13 | 14 | import struct |
|
14 | 15 | |
|
15 | 16 | from .i18n import _ |
@@ -683,7 +684,7 b' class mergestate(object):' | |||
|
683 | 684 | def recordactions(self): |
|
684 | 685 | """record remove/add/get actions in the dirstate""" |
|
685 | 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 | 689 | def queueremove(self, f): |
|
689 | 690 | """queues a file to be removed from the dirstate |
@@ -1464,13 +1465,17 b' def batchremove(repo, wctx, actions):' | |||
|
1464 | 1465 | repo.ui.warn(_("current directory was removed\n" |
|
1465 | 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 | 1469 | """apply gets to the working directory |
|
1469 | 1470 | |
|
1470 | 1471 | mctx is the context to get from |
|
1471 | 1472 | |
|
1472 |
|
|
|
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 | 1479 | verbose = repo.ui.verbose |
|
1475 | 1480 | fctx = mctx.filectx |
|
1476 | 1481 | ui = repo.ui |
@@ -1494,16 +1499,24 b' def batchget(repo, mctx, wctx, actions):' | |||
|
1494 | 1499 | if repo.wvfs.lexists(conflicting): |
|
1495 | 1500 | orig = scmutil.backuppath(ui, repo, conflicting) |
|
1496 | 1501 | util.rename(repo.wjoin(conflicting), orig) |
|
1497 |
wctx[f] |
|
|
1502 | wfctx = wctx[f] | |
|
1503 | wfctx.clearunknown() | |
|
1498 | 1504 | atomictemp = ui.configbool("experimental", "update.atomic-file") |
|
1499 |
wctx |
|
|
1500 |
|
|
|
1505 | size = wfctx.write(fctx(f).data(), flags, | |
|
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 | 1513 | if i == 100: |
|
1502 | yield i, f | |
|
1514 | yield False, (i, f) | |
|
1503 | 1515 | i = 0 |
|
1504 | 1516 | i += 1 |
|
1505 | 1517 | if i > 0: |
|
1506 | yield i, f | |
|
1518 | yield False, (i, f) | |
|
1519 | yield True, filedata | |
|
1507 | 1520 | |
|
1508 | 1521 | def _prefetchfiles(repo, ctx, actions): |
|
1509 | 1522 | """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict |
@@ -1550,14 +1563,17 b' def emptyactions():' | |||
|
1550 | 1563 | ACTION_PATH_CONFLICT, |
|
1551 | 1564 | ACTION_PATH_CONFLICT_RESOLVE)) |
|
1552 | 1565 | |
|
1553 |
def applyupdates(repo, actions, wctx, mctx, overwrite, |
|
|
1566 | def applyupdates(repo, actions, wctx, mctx, overwrite, wantfiledata, | |
|
1567 | labels=None): | |
|
1554 | 1568 | """apply the merge action list to the working directory |
|
1555 | 1569 | |
|
1556 | 1570 | wctx is the working copy context |
|
1557 | 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 | |
|
1560 | describes how many files were affected by the update. | |
|
1573 | Return a tuple of (counts, filedata), where counts is a tuple | |
|
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 | 1579 | _prefetchfiles(repo, mctx, actions) |
@@ -1649,11 +1665,18 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1649 | 1665 | # get in parallel. |
|
1650 | 1666 | threadsafe = repo.ui.configbool('experimental', |
|
1651 | 1667 | 'worker.wdir-get-thread-safe') |
|
1652 |
prog = worker.worker(repo.ui, cost, batchget, |
|
|
1668 | prog = worker.worker(repo.ui, cost, batchget, | |
|
1669 | (repo, mctx, wctx, wantfiledata), | |
|
1653 | 1670 | actions[ACTION_GET], |
|
1654 |
threadsafe=threadsafe |
|
|
1655 | for i, item in prog: | |
|
1656 | progress.increment(step=i, item=item) | |
|
1671 | threadsafe=threadsafe, | |
|
1672 | hasretval=True) | |
|
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 | 1680 | updated = len(actions[ACTION_GET]) |
|
1658 | 1681 | |
|
1659 | 1682 | if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']: |
@@ -1778,6 +1801,9 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1778 | 1801 | mfiles = set(a[0] for a in actions[ACTION_MERGE]) |
|
1779 | 1802 | for k, acts in extraactions.iteritems(): |
|
1780 | 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 | 1807 | # Remove these files from actions[ACTION_MERGE] as well. This is |
|
1782 | 1808 | # important because in recordupdates, files in actions[ACTION_MERGE] |
|
1783 | 1809 | # are processed after files in other actions, and the merge driver |
@@ -1800,9 +1826,10 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1800 | 1826 | if a[0] in mfiles] |
|
1801 | 1827 | |
|
1802 | 1828 | progress.complete() |
|
1803 | return updateresult(updated, merged, removed, unresolved) | |
|
1829 | assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0) | |
|
1830 | return updateresult(updated, merged, removed, unresolved), getfiledata | |
|
1804 | 1831 | |
|
1805 | def recordupdates(repo, actions, branchmerge): | |
|
1832 | def recordupdates(repo, actions, branchmerge, getfiledata): | |
|
1806 | 1833 | "record merge actions to the dirstate" |
|
1807 | 1834 | # remove (must come first) |
|
1808 | 1835 | for f, args, msg in actions.get(ACTION_REMOVE, []): |
@@ -1846,11 +1873,12 b' def recordupdates(repo, actions, branchm' | |||
|
1846 | 1873 | pass |
|
1847 | 1874 | |
|
1848 | 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 | 1877 | if branchmerge: |
|
1851 | 1878 | repo.dirstate.otherparent(f) |
|
1852 | 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 | 1883 | # merge |
|
1856 | 1884 | for f, args, msg in actions.get(ACTION_MERGE, []): |
@@ -2166,12 +2194,15 b' def update(repo, node, branchmerge, forc' | |||
|
2166 | 2194 | 'fsmonitor enabled; enable fsmonitor to improve performance; ' |
|
2167 | 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() | |
|
2198 | wantfiledata = updatedirstate and not branchmerge | |
|
2199 | stats, getfiledata = applyupdates(repo, actions, wc, p2, overwrite, | |
|
2200 | wantfiledata, labels=labels) | |
|
2170 | 2201 | |
|
2171 | if not partial and not wc.isinmemory(): | |
|
2202 | if updatedirstate: | |
|
2172 | 2203 | with repo.dirstate.parentchange(): |
|
2173 | 2204 | repo.setparents(fp1, fp2) |
|
2174 | recordupdates(repo, actions, branchmerge) | |
|
2205 | recordupdates(repo, actions, branchmerge, getfiledata) | |
|
2175 | 2206 | # update completed, clear state |
|
2176 | 2207 | util.unlink(repo.vfs.join('updatestate')) |
|
2177 | 2208 |
@@ -259,7 +259,7 b' def _writeaddedfiles(repo, pctx, files):' | |||
|
259 | 259 | if not repo.wvfs.exists(f): |
|
260 | 260 | addgaction((f, (mf.flags(f), False), "narrowspec updated")) |
|
261 | 261 | merge.applyupdates(repo, actions, wctx=repo[None], |
|
262 | mctx=repo['.'], overwrite=False) | |
|
262 | mctx=repo['.'], overwrite=False, wantfiledata=False) | |
|
263 | 263 | |
|
264 | 264 | def checkworkingcopynarrowspec(repo): |
|
265 | 265 | # Avoid infinite recursion when updating the working copy |
@@ -248,7 +248,8 b' def prunetemporaryincludes(repo):' | |||
|
248 | 248 | |
|
249 | 249 | typeactions = mergemod.emptyactions() |
|
250 | 250 | typeactions['r'] = actions |
|
251 |
mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False |
|
|
251 | mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False, | |
|
252 | wantfiledata=False) | |
|
252 | 253 | |
|
253 | 254 | # Fix dirstate |
|
254 | 255 | for file in dropped: |
@@ -382,7 +383,7 b' def filterupdatesactions(repo, wctx, mct' | |||
|
382 | 383 | typeactions = mergemod.emptyactions() |
|
383 | 384 | typeactions['g'] = actions |
|
384 | 385 | mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], |
|
385 | False) | |
|
386 | False, wantfiledata=False) | |
|
386 | 387 | |
|
387 | 388 | dirstate = repo.dirstate |
|
388 | 389 | for file, flags, msg in actions: |
@@ -486,7 +487,8 b' def refreshwdir(repo, origstatus, origsp' | |||
|
486 | 487 | for f, (m, args, msg) in actions.iteritems(): |
|
487 | 488 | typeactions[m].append((f, args, msg)) |
|
488 | 489 | |
|
489 |
mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False |
|
|
490 | mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False, | |
|
491 | wantfiledata=False) | |
|
490 | 492 | |
|
491 | 493 | # Fix dirstate |
|
492 | 494 | for file in added: |
@@ -27,13 +27,11 b" outside of hg's control." | |||
|
27 | 27 | > EOF |
|
28 | 28 | |
|
29 | 29 | Do an update where file 'a' is changed between hg writing it to disk |
|
30 | and hg writing the dirstate. It results in a corrupted dirstate, which | |
|
31 | stores the wrong size, and thus hg status shows spuriously modified | |
|
32 | files. | |
|
30 | and hg writing the dirstate. The dirstate is correct nonetheless, and | |
|
31 | so hg status correctly shows a as clean. | |
|
33 | 32 | |
|
34 | 33 | $ hg up -r 0 --config extensions.race=$TESTTMP/dirstaterace.py |
|
35 | 34 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
36 | 35 | $ hg debugdirstate --no-dates |
|
37 |
n 644 |
|
|
36 | n 644 2 (set |unset) a (re) | |
|
38 | 37 | $ echo a > a; hg status; hg diff |
|
39 | M a |
General Comments 0
You need to be logged in to leave comments.
Login now