|
1 # narrowdirstate.py - extensions to mercurial dirstate to support narrow clones |
|
2 # |
|
3 # Copyright 2017 Google, Inc. |
|
4 # |
|
5 # This software may be used and distributed according to the terms of the |
|
6 # GNU General Public License version 2 or any later version. |
|
7 |
|
8 from __future__ import absolute_import |
|
9 |
|
10 from mercurial.i18n import _ |
|
11 from mercurial import ( |
|
12 dirstate, |
|
13 error, |
|
14 extensions, |
|
15 match as matchmod, |
|
16 util as hgutil, |
|
17 ) |
|
18 |
|
19 from . import narrowspec |
|
20 |
|
21 def setup(repo): |
|
22 """Add narrow spec dirstate ignore, block changes outside narrow spec.""" |
|
23 |
|
24 def walk(orig, self, match, subrepos, unknown, ignored, full=True, |
|
25 narrowonly=True): |
|
26 if narrowonly: |
|
27 narrowmatch = repo.narrowmatch() |
|
28 match = matchmod.intersectmatchers(match, narrowmatch) |
|
29 return orig(self, match, subrepos, unknown, ignored, full) |
|
30 |
|
31 extensions.wrapfunction(dirstate.dirstate, 'walk', walk) |
|
32 |
|
33 # Prevent adding files that are outside the sparse checkout |
|
34 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge'] |
|
35 for func in editfuncs: |
|
36 def _wrapper(orig, self, *args): |
|
37 dirstate = repo.dirstate |
|
38 narrowmatch = repo.narrowmatch() |
|
39 for f in args: |
|
40 if f is not None and not narrowmatch(f) and f not in dirstate: |
|
41 raise error.Abort(_("cannot track '%s' - it is outside " + |
|
42 "the narrow clone") % f) |
|
43 return orig(self, *args) |
|
44 extensions.wrapfunction(dirstate.dirstate, func, _wrapper) |
|
45 |
|
46 def filterrebuild(orig, self, parent, allfiles, changedfiles=None): |
|
47 if changedfiles is None: |
|
48 # Rebuilding entire dirstate, let's filter allfiles to match the |
|
49 # narrowspec. |
|
50 allfiles = [f for f in allfiles if repo.narrowmatch()(f)] |
|
51 orig(self, parent, allfiles, changedfiles) |
|
52 |
|
53 extensions.wrapfunction(dirstate.dirstate, 'rebuild', filterrebuild) |
|
54 |
|
55 def _narrowbackupname(backupname): |
|
56 assert 'dirstate' in backupname |
|
57 return backupname.replace('dirstate', narrowspec.FILENAME) |
|
58 |
|
59 def restorebackup(orig, self, tr, backupname): |
|
60 self._opener.rename(_narrowbackupname(backupname), narrowspec.FILENAME, |
|
61 checkambig=True) |
|
62 orig(self, tr, backupname) |
|
63 |
|
64 extensions.wrapfunction(dirstate.dirstate, 'restorebackup', restorebackup) |
|
65 |
|
66 def savebackup(orig, self, tr, backupname): |
|
67 orig(self, tr, backupname) |
|
68 |
|
69 narrowbackupname = _narrowbackupname(backupname) |
|
70 self._opener.tryunlink(narrowbackupname) |
|
71 hgutil.copyfile(self._opener.join(narrowspec.FILENAME), |
|
72 self._opener.join(narrowbackupname), hardlink=True) |
|
73 |
|
74 extensions.wrapfunction(dirstate.dirstate, 'savebackup', savebackup) |
|
75 |
|
76 def clearbackup(orig, self, tr, backupname): |
|
77 orig(self, tr, backupname) |
|
78 self._opener.unlink(_narrowbackupname(backupname)) |
|
79 |
|
80 extensions.wrapfunction(dirstate.dirstate, 'clearbackup', clearbackup) |