##// END OF EJS Templates
sparse: override dirstate.walk() instead of dirstate._ignore...
Martin von Zweigbergk -
r33496:258298f4 default
parent child Browse files
Show More
@@ -1,362 +1,337
1 # sparse.py - allow sparse checkouts of the working directory
1 # sparse.py - allow sparse checkouts of the working directory
2 #
2 #
3 # Copyright 2014 Facebook, Inc.
3 # Copyright 2014 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
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.
6 # GNU General Public License version 2 or any later version.
7
7
8 """allow sparse checkouts of the working directory (EXPERIMENTAL)
8 """allow sparse checkouts of the working directory (EXPERIMENTAL)
9
9
10 (This extension is not yet protected by backwards compatibility
10 (This extension is not yet protected by backwards compatibility
11 guarantees. Any aspect may break in future releases until this
11 guarantees. Any aspect may break in future releases until this
12 notice is removed.)
12 notice is removed.)
13
13
14 This extension allows the working directory to only consist of a
14 This extension allows the working directory to only consist of a
15 subset of files for the revision. This allows specific files or
15 subset of files for the revision. This allows specific files or
16 directories to be explicitly included or excluded. Many repository
16 directories to be explicitly included or excluded. Many repository
17 operations have performance proportional to the number of files in
17 operations have performance proportional to the number of files in
18 the working directory. So only realizing a subset of files in the
18 the working directory. So only realizing a subset of files in the
19 working directory can improve performance.
19 working directory can improve performance.
20
20
21 Sparse Config Files
21 Sparse Config Files
22 -------------------
22 -------------------
23
23
24 The set of files that are part of a sparse checkout are defined by
24 The set of files that are part of a sparse checkout are defined by
25 a sparse config file. The file defines 3 things: includes (files to
25 a sparse config file. The file defines 3 things: includes (files to
26 include in the sparse checkout), excludes (files to exclude from the
26 include in the sparse checkout), excludes (files to exclude from the
27 sparse checkout), and profiles (links to other config files).
27 sparse checkout), and profiles (links to other config files).
28
28
29 The file format is newline delimited. Empty lines and lines beginning
29 The file format is newline delimited. Empty lines and lines beginning
30 with ``#`` are ignored.
30 with ``#`` are ignored.
31
31
32 Lines beginning with ``%include `` denote another sparse config file
32 Lines beginning with ``%include `` denote another sparse config file
33 to include. e.g. ``%include tests.sparse``. The filename is relative
33 to include. e.g. ``%include tests.sparse``. The filename is relative
34 to the repository root.
34 to the repository root.
35
35
36 The special lines ``[include]`` and ``[exclude]`` denote the section
36 The special lines ``[include]`` and ``[exclude]`` denote the section
37 for includes and excludes that follow, respectively. It is illegal to
37 for includes and excludes that follow, respectively. It is illegal to
38 have ``[include]`` after ``[exclude]``. If no sections are defined,
38 have ``[include]`` after ``[exclude]``. If no sections are defined,
39 entries are assumed to be in the ``[include]`` section.
39 entries are assumed to be in the ``[include]`` section.
40
40
41 Non-special lines resemble file patterns to be added to either includes
41 Non-special lines resemble file patterns to be added to either includes
42 or excludes. The syntax of these lines is documented by :hg:`help patterns`.
42 or excludes. The syntax of these lines is documented by :hg:`help patterns`.
43 Patterns are interpreted as ``glob:`` by default and match against the
43 Patterns are interpreted as ``glob:`` by default and match against the
44 root of the repository.
44 root of the repository.
45
45
46 Exclusion patterns take precedence over inclusion patterns. So even
46 Exclusion patterns take precedence over inclusion patterns. So even
47 if a file is explicitly included, an ``[exclude]`` entry can remove it.
47 if a file is explicitly included, an ``[exclude]`` entry can remove it.
48
48
49 For example, say you have a repository with 3 directories, ``frontend/``,
49 For example, say you have a repository with 3 directories, ``frontend/``,
50 ``backend/``, and ``tools/``. ``frontend/`` and ``backend/`` correspond
50 ``backend/``, and ``tools/``. ``frontend/`` and ``backend/`` correspond
51 to different projects and it is uncommon for someone working on one
51 to different projects and it is uncommon for someone working on one
52 to need the files for the other. But ``tools/`` contains files shared
52 to need the files for the other. But ``tools/`` contains files shared
53 between both projects. Your sparse config files may resemble::
53 between both projects. Your sparse config files may resemble::
54
54
55 # frontend.sparse
55 # frontend.sparse
56 frontend/**
56 frontend/**
57 tools/**
57 tools/**
58
58
59 # backend.sparse
59 # backend.sparse
60 backend/**
60 backend/**
61 tools/**
61 tools/**
62
62
63 Say the backend grows in size. Or there's a directory with thousands
63 Say the backend grows in size. Or there's a directory with thousands
64 of files you wish to exclude. You can modify the profile to exclude
64 of files you wish to exclude. You can modify the profile to exclude
65 certain files::
65 certain files::
66
66
67 [include]
67 [include]
68 backend/**
68 backend/**
69 tools/**
69 tools/**
70
70
71 [exclude]
71 [exclude]
72 tools/tests/**
72 tools/tests/**
73 """
73 """
74
74
75 from __future__ import absolute_import
75 from __future__ import absolute_import
76
76
77 from mercurial.i18n import _
77 from mercurial.i18n import _
78 from mercurial import (
78 from mercurial import (
79 cmdutil,
79 cmdutil,
80 commands,
80 commands,
81 dirstate,
81 dirstate,
82 error,
82 error,
83 extensions,
83 extensions,
84 hg,
84 hg,
85 match as matchmod,
85 match as matchmod,
86 registrar,
86 registrar,
87 sparse,
87 sparse,
88 util,
88 util,
89 )
89 )
90
90
91 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
91 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
92 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
92 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
93 # be specifying the version(s) of Mercurial they are tested with, or
93 # be specifying the version(s) of Mercurial they are tested with, or
94 # leave the attribute unspecified.
94 # leave the attribute unspecified.
95 testedwith = 'ships-with-hg-core'
95 testedwith = 'ships-with-hg-core'
96
96
97 cmdtable = {}
97 cmdtable = {}
98 command = registrar.command(cmdtable)
98 command = registrar.command(cmdtable)
99
99
100 def extsetup(ui):
100 def extsetup(ui):
101 sparse.enabled = True
101 sparse.enabled = True
102
102
103 _setupclone(ui)
103 _setupclone(ui)
104 _setuplog(ui)
104 _setuplog(ui)
105 _setupadd(ui)
105 _setupadd(ui)
106 _setupdirstate(ui)
106 _setupdirstate(ui)
107
107
108 def replacefilecache(cls, propname, replacement):
108 def replacefilecache(cls, propname, replacement):
109 """Replace a filecache property with a new class. This allows changing the
109 """Replace a filecache property with a new class. This allows changing the
110 cache invalidation condition."""
110 cache invalidation condition."""
111 origcls = cls
111 origcls = cls
112 assert callable(replacement)
112 assert callable(replacement)
113 while cls is not object:
113 while cls is not object:
114 if propname in cls.__dict__:
114 if propname in cls.__dict__:
115 orig = cls.__dict__[propname]
115 orig = cls.__dict__[propname]
116 setattr(cls, propname, replacement(orig))
116 setattr(cls, propname, replacement(orig))
117 break
117 break
118 cls = cls.__bases__[0]
118 cls = cls.__bases__[0]
119
119
120 if cls is object:
120 if cls is object:
121 raise AttributeError(_("type '%s' has no property '%s'") % (origcls,
121 raise AttributeError(_("type '%s' has no property '%s'") % (origcls,
122 propname))
122 propname))
123
123
124 def _setuplog(ui):
124 def _setuplog(ui):
125 entry = commands.table['^log|history']
125 entry = commands.table['^log|history']
126 entry[1].append(('', 'sparse', None,
126 entry[1].append(('', 'sparse', None,
127 "limit to changesets affecting the sparse checkout"))
127 "limit to changesets affecting the sparse checkout"))
128
128
129 def _logrevs(orig, repo, opts):
129 def _logrevs(orig, repo, opts):
130 revs = orig(repo, opts)
130 revs = orig(repo, opts)
131 if opts.get('sparse'):
131 if opts.get('sparse'):
132 sparsematch = sparse.matcher(repo)
132 sparsematch = sparse.matcher(repo)
133 def ctxmatch(rev):
133 def ctxmatch(rev):
134 ctx = repo[rev]
134 ctx = repo[rev]
135 return any(f for f in ctx.files() if sparsematch(f))
135 return any(f for f in ctx.files() if sparsematch(f))
136 revs = revs.filter(ctxmatch)
136 revs = revs.filter(ctxmatch)
137 return revs
137 return revs
138 extensions.wrapfunction(cmdutil, '_logrevs', _logrevs)
138 extensions.wrapfunction(cmdutil, '_logrevs', _logrevs)
139
139
140 def _clonesparsecmd(orig, ui, repo, *args, **opts):
140 def _clonesparsecmd(orig, ui, repo, *args, **opts):
141 include_pat = opts.get('include')
141 include_pat = opts.get('include')
142 exclude_pat = opts.get('exclude')
142 exclude_pat = opts.get('exclude')
143 enableprofile_pat = opts.get('enable_profile')
143 enableprofile_pat = opts.get('enable_profile')
144 include = exclude = enableprofile = False
144 include = exclude = enableprofile = False
145 if include_pat:
145 if include_pat:
146 pat = include_pat
146 pat = include_pat
147 include = True
147 include = True
148 if exclude_pat:
148 if exclude_pat:
149 pat = exclude_pat
149 pat = exclude_pat
150 exclude = True
150 exclude = True
151 if enableprofile_pat:
151 if enableprofile_pat:
152 pat = enableprofile_pat
152 pat = enableprofile_pat
153 enableprofile = True
153 enableprofile = True
154 if sum([include, exclude, enableprofile]) > 1:
154 if sum([include, exclude, enableprofile]) > 1:
155 raise error.Abort(_("too many flags specified."))
155 raise error.Abort(_("too many flags specified."))
156 if include or exclude or enableprofile:
156 if include or exclude or enableprofile:
157 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
157 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
158 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
158 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
159 exclude=exclude, enableprofile=enableprofile)
159 exclude=exclude, enableprofile=enableprofile)
160 return orig(self, node, overwrite, *args, **kwargs)
160 return orig(self, node, overwrite, *args, **kwargs)
161 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
161 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
162 return orig(ui, repo, *args, **opts)
162 return orig(ui, repo, *args, **opts)
163
163
164 def _setupclone(ui):
164 def _setupclone(ui):
165 entry = commands.table['^clone']
165 entry = commands.table['^clone']
166 entry[1].append(('', 'enable-profile', [],
166 entry[1].append(('', 'enable-profile', [],
167 'enable a sparse profile'))
167 'enable a sparse profile'))
168 entry[1].append(('', 'include', [],
168 entry[1].append(('', 'include', [],
169 'include sparse pattern'))
169 'include sparse pattern'))
170 entry[1].append(('', 'exclude', [],
170 entry[1].append(('', 'exclude', [],
171 'exclude sparse pattern'))
171 'exclude sparse pattern'))
172 extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
172 extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
173
173
174 def _setupadd(ui):
174 def _setupadd(ui):
175 entry = commands.table['^add']
175 entry = commands.table['^add']
176 entry[1].append(('s', 'sparse', None,
176 entry[1].append(('s', 'sparse', None,
177 'also include directories of added files in sparse config'))
177 'also include directories of added files in sparse config'))
178
178
179 def _add(orig, ui, repo, *pats, **opts):
179 def _add(orig, ui, repo, *pats, **opts):
180 if opts.get('sparse'):
180 if opts.get('sparse'):
181 dirs = set()
181 dirs = set()
182 for pat in pats:
182 for pat in pats:
183 dirname, basename = util.split(pat)
183 dirname, basename = util.split(pat)
184 dirs.add(dirname)
184 dirs.add(dirname)
185 sparse.updateconfig(repo, list(dirs), opts, include=True)
185 sparse.updateconfig(repo, list(dirs), opts, include=True)
186 return orig(ui, repo, *pats, **opts)
186 return orig(ui, repo, *pats, **opts)
187
187
188 extensions.wrapcommand(commands.table, 'add', _add)
188 extensions.wrapcommand(commands.table, 'add', _add)
189
189
190 def _setupdirstate(ui):
190 def _setupdirstate(ui):
191 """Modify the dirstate to prevent stat'ing excluded files,
191 """Modify the dirstate to prevent stat'ing excluded files,
192 and to prevent modifications to files outside the checkout.
192 and to prevent modifications to files outside the checkout.
193 """
193 """
194
194
195 # The atrocity below is needed to wrap dirstate._ignore. It is a cached
195 def walk(orig, self, match, subrepos, unknown, ignored, full=True):
196 # property, which means normal function wrapping doesn't work.
196 match = matchmod.intersectmatchers(match, self._sparsematcher)
197 class ignorewrapper(object):
197 return orig(self, match, subrepos, unknown, ignored, full)
198 def __init__(self, orig):
199 self.orig = orig
200 self.origignore = None
201 self.func = None
202 self.sparsematch = None
203
204 def __get__(self, obj, type=None):
205 origignore = self.orig.__get__(obj)
206
198
207 sparsematch = obj._sparsematcher
199 extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
208 if sparsematch.always():
209 return origignore
210
211 if self.sparsematch != sparsematch or self.origignore != origignore:
212 self.func = matchmod.unionmatcher([
213 origignore, matchmod.negatematcher(sparsematch)])
214 self.sparsematch = sparsematch
215 self.origignore = origignore
216 return self.func
217
218 def __set__(self, obj, value):
219 return self.orig.__set__(obj, value)
220
221 def __delete__(self, obj):
222 return self.orig.__delete__(obj)
223
224 replacefilecache(dirstate.dirstate, '_ignore', ignorewrapper)
225
200
226 # dirstate.rebuild should not add non-matching files
201 # dirstate.rebuild should not add non-matching files
227 def _rebuild(orig, self, parent, allfiles, changedfiles=None):
202 def _rebuild(orig, self, parent, allfiles, changedfiles=None):
228 matcher = self._sparsematcher
203 matcher = self._sparsematcher
229 if not matcher.always():
204 if not matcher.always():
230 allfiles = allfiles.matches(matcher)
205 allfiles = allfiles.matches(matcher)
231 if changedfiles:
206 if changedfiles:
232 changedfiles = [f for f in changedfiles if matcher(f)]
207 changedfiles = [f for f in changedfiles if matcher(f)]
233
208
234 if changedfiles is not None:
209 if changedfiles is not None:
235 # In _rebuild, these files will be deleted from the dirstate
210 # In _rebuild, these files will be deleted from the dirstate
236 # when they are not found to be in allfiles
211 # when they are not found to be in allfiles
237 dirstatefilestoremove = set(f for f in self if not matcher(f))
212 dirstatefilestoremove = set(f for f in self if not matcher(f))
238 changedfiles = dirstatefilestoremove.union(changedfiles)
213 changedfiles = dirstatefilestoremove.union(changedfiles)
239
214
240 return orig(self, parent, allfiles, changedfiles)
215 return orig(self, parent, allfiles, changedfiles)
241 extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
216 extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
242
217
243 # Prevent adding files that are outside the sparse checkout
218 # Prevent adding files that are outside the sparse checkout
244 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
219 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
245 hint = _('include file with `hg debugsparse --include <pattern>` or use ' +
220 hint = _('include file with `hg debugsparse --include <pattern>` or use ' +
246 '`hg add -s <file>` to include file directory while adding')
221 '`hg add -s <file>` to include file directory while adding')
247 for func in editfuncs:
222 for func in editfuncs:
248 def _wrapper(orig, self, *args):
223 def _wrapper(orig, self, *args):
249 sparsematch = self._sparsematcher
224 sparsematch = self._sparsematcher
250 if not sparsematch.always():
225 if not sparsematch.always():
251 for f in args:
226 for f in args:
252 if (f is not None and not sparsematch(f) and
227 if (f is not None and not sparsematch(f) and
253 f not in self):
228 f not in self):
254 raise error.Abort(_("cannot add '%s' - it is outside "
229 raise error.Abort(_("cannot add '%s' - it is outside "
255 "the sparse checkout") % f,
230 "the sparse checkout") % f,
256 hint=hint)
231 hint=hint)
257 return orig(self, *args)
232 return orig(self, *args)
258 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
233 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
259
234
260 @command('^debugsparse', [
235 @command('^debugsparse', [
261 ('I', 'include', False, _('include files in the sparse checkout')),
236 ('I', 'include', False, _('include files in the sparse checkout')),
262 ('X', 'exclude', False, _('exclude files in the sparse checkout')),
237 ('X', 'exclude', False, _('exclude files in the sparse checkout')),
263 ('d', 'delete', False, _('delete an include/exclude rule')),
238 ('d', 'delete', False, _('delete an include/exclude rule')),
264 ('f', 'force', False, _('allow changing rules even with pending changes')),
239 ('f', 'force', False, _('allow changing rules even with pending changes')),
265 ('', 'enable-profile', False, _('enables the specified profile')),
240 ('', 'enable-profile', False, _('enables the specified profile')),
266 ('', 'disable-profile', False, _('disables the specified profile')),
241 ('', 'disable-profile', False, _('disables the specified profile')),
267 ('', 'import-rules', False, _('imports rules from a file')),
242 ('', 'import-rules', False, _('imports rules from a file')),
268 ('', 'clear-rules', False, _('clears local include/exclude rules')),
243 ('', 'clear-rules', False, _('clears local include/exclude rules')),
269 ('', 'refresh', False, _('updates the working after sparseness changes')),
244 ('', 'refresh', False, _('updates the working after sparseness changes')),
270 ('', 'reset', False, _('makes the repo full again')),
245 ('', 'reset', False, _('makes the repo full again')),
271 ] + commands.templateopts,
246 ] + commands.templateopts,
272 _('[--OPTION] PATTERN...'))
247 _('[--OPTION] PATTERN...'))
273 def debugsparse(ui, repo, *pats, **opts):
248 def debugsparse(ui, repo, *pats, **opts):
274 """make the current checkout sparse, or edit the existing checkout
249 """make the current checkout sparse, or edit the existing checkout
275
250
276 The sparse command is used to make the current checkout sparse.
251 The sparse command is used to make the current checkout sparse.
277 This means files that don't meet the sparse condition will not be
252 This means files that don't meet the sparse condition will not be
278 written to disk, or show up in any working copy operations. It does
253 written to disk, or show up in any working copy operations. It does
279 not affect files in history in any way.
254 not affect files in history in any way.
280
255
281 Passing no arguments prints the currently applied sparse rules.
256 Passing no arguments prints the currently applied sparse rules.
282
257
283 --include and --exclude are used to add and remove files from the sparse
258 --include and --exclude are used to add and remove files from the sparse
284 checkout. The effects of adding an include or exclude rule are applied
259 checkout. The effects of adding an include or exclude rule are applied
285 immediately. If applying the new rule would cause a file with pending
260 immediately. If applying the new rule would cause a file with pending
286 changes to be added or removed, the command will fail. Pass --force to
261 changes to be added or removed, the command will fail. Pass --force to
287 force a rule change even with pending changes (the changes on disk will
262 force a rule change even with pending changes (the changes on disk will
288 be preserved).
263 be preserved).
289
264
290 --delete removes an existing include/exclude rule. The effects are
265 --delete removes an existing include/exclude rule. The effects are
291 immediate.
266 immediate.
292
267
293 --refresh refreshes the files on disk based on the sparse rules. This is
268 --refresh refreshes the files on disk based on the sparse rules. This is
294 only necessary if .hg/sparse was changed by hand.
269 only necessary if .hg/sparse was changed by hand.
295
270
296 --enable-profile and --disable-profile accept a path to a .hgsparse file.
271 --enable-profile and --disable-profile accept a path to a .hgsparse file.
297 This allows defining sparse checkouts and tracking them inside the
272 This allows defining sparse checkouts and tracking them inside the
298 repository. This is useful for defining commonly used sparse checkouts for
273 repository. This is useful for defining commonly used sparse checkouts for
299 many people to use. As the profile definition changes over time, the sparse
274 many people to use. As the profile definition changes over time, the sparse
300 checkout will automatically be updated appropriately, depending on which
275 checkout will automatically be updated appropriately, depending on which
301 changeset is checked out. Changes to .hgsparse are not applied until they
276 changeset is checked out. Changes to .hgsparse are not applied until they
302 have been committed.
277 have been committed.
303
278
304 --import-rules accepts a path to a file containing rules in the .hgsparse
279 --import-rules accepts a path to a file containing rules in the .hgsparse
305 format, allowing you to add --include, --exclude and --enable-profile rules
280 format, allowing you to add --include, --exclude and --enable-profile rules
306 in bulk. Like the --include, --exclude and --enable-profile switches, the
281 in bulk. Like the --include, --exclude and --enable-profile switches, the
307 changes are applied immediately.
282 changes are applied immediately.
308
283
309 --clear-rules removes all local include and exclude rules, while leaving
284 --clear-rules removes all local include and exclude rules, while leaving
310 any enabled profiles in place.
285 any enabled profiles in place.
311
286
312 Returns 0 if editing the sparse checkout succeeds.
287 Returns 0 if editing the sparse checkout succeeds.
313 """
288 """
314 include = opts.get('include')
289 include = opts.get('include')
315 exclude = opts.get('exclude')
290 exclude = opts.get('exclude')
316 force = opts.get('force')
291 force = opts.get('force')
317 enableprofile = opts.get('enable_profile')
292 enableprofile = opts.get('enable_profile')
318 disableprofile = opts.get('disable_profile')
293 disableprofile = opts.get('disable_profile')
319 importrules = opts.get('import_rules')
294 importrules = opts.get('import_rules')
320 clearrules = opts.get('clear_rules')
295 clearrules = opts.get('clear_rules')
321 delete = opts.get('delete')
296 delete = opts.get('delete')
322 refresh = opts.get('refresh')
297 refresh = opts.get('refresh')
323 reset = opts.get('reset')
298 reset = opts.get('reset')
324 count = sum([include, exclude, enableprofile, disableprofile, delete,
299 count = sum([include, exclude, enableprofile, disableprofile, delete,
325 importrules, refresh, clearrules, reset])
300 importrules, refresh, clearrules, reset])
326 if count > 1:
301 if count > 1:
327 raise error.Abort(_("too many flags specified"))
302 raise error.Abort(_("too many flags specified"))
328
303
329 if count == 0:
304 if count == 0:
330 if repo.vfs.exists('sparse'):
305 if repo.vfs.exists('sparse'):
331 ui.status(repo.vfs.read("sparse") + "\n")
306 ui.status(repo.vfs.read("sparse") + "\n")
332 temporaryincludes = sparse.readtemporaryincludes(repo)
307 temporaryincludes = sparse.readtemporaryincludes(repo)
333 if temporaryincludes:
308 if temporaryincludes:
334 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
309 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
335 ui.status(("\n".join(temporaryincludes) + "\n"))
310 ui.status(("\n".join(temporaryincludes) + "\n"))
336 else:
311 else:
337 ui.status(_('repo is not sparse\n'))
312 ui.status(_('repo is not sparse\n'))
338 return
313 return
339
314
340 if include or exclude or delete or reset or enableprofile or disableprofile:
315 if include or exclude or delete or reset or enableprofile or disableprofile:
341 sparse.updateconfig(repo, pats, opts, include=include, exclude=exclude,
316 sparse.updateconfig(repo, pats, opts, include=include, exclude=exclude,
342 reset=reset, delete=delete,
317 reset=reset, delete=delete,
343 enableprofile=enableprofile,
318 enableprofile=enableprofile,
344 disableprofile=disableprofile, force=force)
319 disableprofile=disableprofile, force=force)
345
320
346 if importrules:
321 if importrules:
347 sparse.importfromfiles(repo, opts, pats, force=force)
322 sparse.importfromfiles(repo, opts, pats, force=force)
348
323
349 if clearrules:
324 if clearrules:
350 sparse.clearrules(repo, force=force)
325 sparse.clearrules(repo, force=force)
351
326
352 if refresh:
327 if refresh:
353 try:
328 try:
354 wlock = repo.wlock()
329 wlock = repo.wlock()
355 fcounts = map(
330 fcounts = map(
356 len,
331 len,
357 sparse.refreshwdir(repo, repo.status(), sparse.matcher(repo),
332 sparse.refreshwdir(repo, repo.status(), sparse.matcher(repo),
358 force=force))
333 force=force))
359 sparse.printchanges(ui, opts, added=fcounts[0], dropped=fcounts[1],
334 sparse.printchanges(ui, opts, added=fcounts[0], dropped=fcounts[1],
360 conflicting=fcounts[2])
335 conflicting=fcounts[2])
361 finally:
336 finally:
362 wlock.release()
337 wlock.release()
@@ -1,365 +1,370
1 test sparse
1 test sparse
2
2
3 $ hg init myrepo
3 $ hg init myrepo
4 $ cd myrepo
4 $ cd myrepo
5 $ cat > .hg/hgrc <<EOF
5 $ cat > .hg/hgrc <<EOF
6 > [extensions]
6 > [extensions]
7 > sparse=
7 > sparse=
8 > strip=
8 > strip=
9 > EOF
9 > EOF
10
10
11 $ echo a > show
11 $ echo a > show
12 $ echo x > hide
12 $ echo x > hide
13 $ hg ci -Aqm 'initial'
13 $ hg ci -Aqm 'initial'
14
14
15 $ echo b > show
15 $ echo b > show
16 $ echo y > hide
16 $ echo y > hide
17 $ echo aa > show2
17 $ echo aa > show2
18 $ echo xx > hide2
18 $ echo xx > hide2
19 $ hg ci -Aqm 'two'
19 $ hg ci -Aqm 'two'
20
20
21 Verify basic --include
21 Verify basic --include
22
22
23 $ hg up -q 0
23 $ hg up -q 0
24 $ hg debugsparse --include 'hide'
24 $ hg debugsparse --include 'hide'
25 $ ls
25 $ ls
26 hide
26 hide
27
27
28 Absolute paths outside the repo should just be rejected
28 Absolute paths outside the repo should just be rejected
29
29
30 #if no-windows
30 #if no-windows
31 $ hg debugsparse --include /foo/bar
31 $ hg debugsparse --include /foo/bar
32 warning: paths cannot start with /, ignoring: ['/foo/bar']
32 warning: paths cannot start with /, ignoring: ['/foo/bar']
33 $ hg debugsparse --include '$TESTTMP/myrepo/hide'
33 $ hg debugsparse --include '$TESTTMP/myrepo/hide'
34
34
35 $ hg debugsparse --include '/root'
35 $ hg debugsparse --include '/root'
36 warning: paths cannot start with /, ignoring: ['/root']
36 warning: paths cannot start with /, ignoring: ['/root']
37 #else
37 #else
38 TODO: See if this can be made to fail the same way as on Unix
38 TODO: See if this can be made to fail the same way as on Unix
39 $ hg debugsparse --include /c/foo/bar
39 $ hg debugsparse --include /c/foo/bar
40 abort: c:/foo/bar not under root '$TESTTMP/myrepo' (glob)
40 abort: c:/foo/bar not under root '$TESTTMP/myrepo' (glob)
41 [255]
41 [255]
42 $ hg debugsparse --include '$TESTTMP/myrepo/hide'
42 $ hg debugsparse --include '$TESTTMP/myrepo/hide'
43
43
44 $ hg debugsparse --include '/c/root'
44 $ hg debugsparse --include '/c/root'
45 abort: c:/root not under root '$TESTTMP/myrepo' (glob)
45 abort: c:/root not under root '$TESTTMP/myrepo' (glob)
46 [255]
46 [255]
47 #endif
47 #endif
48
48
49 Verify commiting while sparse includes other files
49 Verify commiting while sparse includes other files
50
50
51 $ echo z > hide
51 $ echo z > hide
52 $ hg ci -Aqm 'edit hide'
52 $ hg ci -Aqm 'edit hide'
53 $ ls
53 $ ls
54 hide
54 hide
55 $ hg manifest
55 $ hg manifest
56 hide
56 hide
57 show
57 show
58
58
59 Verify --reset brings files back
59 Verify --reset brings files back
60
60
61 $ hg debugsparse --reset
61 $ hg debugsparse --reset
62 $ ls
62 $ ls
63 hide
63 hide
64 show
64 show
65 $ cat hide
65 $ cat hide
66 z
66 z
67 $ cat show
67 $ cat show
68 a
68 a
69
69
70 Verify 'hg debugsparse' default output
70 Verify 'hg debugsparse' default output
71
71
72 $ hg up -q null
72 $ hg up -q null
73 $ hg debugsparse --include 'show*'
73 $ hg debugsparse --include 'show*'
74
74
75 $ hg debugsparse
75 $ hg debugsparse
76 [include]
76 [include]
77 show*
77 show*
78
78
79 Verify update only writes included files
79 Verify update only writes included files
80
80
81 $ hg up -q 0
81 $ hg up -q 0
82 $ ls
82 $ ls
83 show
83 show
84
84
85 $ hg up -q 1
85 $ hg up -q 1
86 $ ls
86 $ ls
87 show
87 show
88 show2
88 show2
89
89
90 Verify status only shows included files
90 Verify status only shows included files
91
91
92 $ touch hide
92 $ touch hide
93 $ touch hide3
93 $ touch hide3
94 $ echo c > show
94 $ echo c > show
95 $ hg status
95 $ hg status
96 M show
96 M show
97
97
98 Adding an excluded file should fail
98 Adding an excluded file should fail
99
99
100 $ hg add hide3
100 $ hg add hide3
101 abort: cannot add 'hide3' - it is outside the sparse checkout
101 abort: cannot add 'hide3' - it is outside the sparse checkout
102 (include file with `hg debugsparse --include <pattern>` or use `hg add -s <file>` to include file directory while adding)
102 (include file with `hg debugsparse --include <pattern>` or use `hg add -s <file>` to include file directory while adding)
103 [255]
103 [255]
104
104
105 Verify deleting sparseness while a file has changes fails
105 Verify deleting sparseness while a file has changes fails
106
106
107 $ hg debugsparse --delete 'show*'
107 $ hg debugsparse --delete 'show*'
108 pending changes to 'hide'
108 pending changes to 'hide'
109 abort: cannot change sparseness due to pending changes (delete the files or use --force to bring them back dirty)
109 abort: cannot change sparseness due to pending changes (delete the files or use --force to bring them back dirty)
110 [255]
110 [255]
111
111
112 Verify deleting sparseness with --force brings back files
112 Verify deleting sparseness with --force brings back files
113
113
114 $ hg debugsparse --delete -f 'show*'
114 $ hg debugsparse --delete -f 'show*'
115 pending changes to 'hide'
115 pending changes to 'hide'
116 $ ls
116 $ ls
117 hide
117 hide
118 hide2
118 hide2
119 hide3
119 hide3
120 show
120 show
121 show2
121 show2
122 $ hg st
122 $ hg st
123 M hide
123 M hide
124 M show
124 M show
125 ? hide3
125 ? hide3
126
126
127 Verify editing sparseness fails if pending changes
127 Verify editing sparseness fails if pending changes
128
128
129 $ hg debugsparse --include 'show*'
129 $ hg debugsparse --include 'show*'
130 pending changes to 'hide'
130 pending changes to 'hide'
131 abort: could not update sparseness due to pending changes
131 abort: could not update sparseness due to pending changes
132 [255]
132 [255]
133
133
134 Verify adding sparseness hides files
134 Verify adding sparseness hides files
135
135
136 $ hg debugsparse --exclude -f 'hide*'
136 $ hg debugsparse --exclude -f 'hide*'
137 pending changes to 'hide'
137 pending changes to 'hide'
138 $ ls
138 $ ls
139 hide
139 hide
140 hide3
140 hide3
141 show
141 show
142 show2
142 show2
143 $ hg st
143 $ hg st
144 M show
144 M show
145
145
146 $ hg up -qC .
146 $ hg up -qC .
147 TODO: add an option to purge to also purge files outside the sparse config?
147 $ hg purge --all --config extensions.purge=
148 $ hg purge --all --config extensions.purge=
148 $ ls
149 $ ls
150 hide
151 hide3
149 show
152 show
150 show2
153 show2
154 For now, manually remove the files
155 $ rm hide hide3
151
156
152 Verify rebase temporarily includes excluded files
157 Verify rebase temporarily includes excluded files
153
158
154 $ hg rebase -d 1 -r 2 --config extensions.rebase=
159 $ hg rebase -d 1 -r 2 --config extensions.rebase=
155 rebasing 2:b91df4f39e75 "edit hide" (tip)
160 rebasing 2:b91df4f39e75 "edit hide" (tip)
156 temporarily included 1 file(s) in the sparse checkout for merging
161 temporarily included 1 file(s) in the sparse checkout for merging
157 merging hide
162 merging hide
158 warning: conflicts while merging hide! (edit, then use 'hg resolve --mark')
163 warning: conflicts while merging hide! (edit, then use 'hg resolve --mark')
159 unresolved conflicts (see hg resolve, then hg rebase --continue)
164 unresolved conflicts (see hg resolve, then hg rebase --continue)
160 [1]
165 [1]
161
166
162 $ hg debugsparse
167 $ hg debugsparse
163 [exclude]
168 [exclude]
164 hide*
169 hide*
165
170
166 Temporarily Included Files (for merge/rebase):
171 Temporarily Included Files (for merge/rebase):
167 hide
172 hide
168
173
169 $ cat hide
174 $ cat hide
170 <<<<<<< dest: 39278f7c08a9 - test: two
175 <<<<<<< dest: 39278f7c08a9 - test: two
171 y
176 y
172 =======
177 =======
173 z
178 z
174 >>>>>>> source: b91df4f39e75 - test: edit hide
179 >>>>>>> source: b91df4f39e75 - test: edit hide
175
180
176 Verify aborting a rebase cleans up temporary files
181 Verify aborting a rebase cleans up temporary files
177
182
178 $ hg rebase --abort --config extensions.rebase=
183 $ hg rebase --abort --config extensions.rebase=
179 cleaned up 1 temporarily added file(s) from the sparse checkout
184 cleaned up 1 temporarily added file(s) from the sparse checkout
180 rebase aborted
185 rebase aborted
181 $ rm hide.orig
186 $ rm hide.orig
182
187
183 $ ls
188 $ ls
184 show
189 show
185 show2
190 show2
186
191
187 Verify merge fails if merging excluded files
192 Verify merge fails if merging excluded files
188
193
189 $ hg up -q 1
194 $ hg up -q 1
190 $ hg merge -r 2
195 $ hg merge -r 2
191 temporarily included 1 file(s) in the sparse checkout for merging
196 temporarily included 1 file(s) in the sparse checkout for merging
192 merging hide
197 merging hide
193 warning: conflicts while merging hide! (edit, then use 'hg resolve --mark')
198 warning: conflicts while merging hide! (edit, then use 'hg resolve --mark')
194 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
199 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
195 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
200 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
196 [1]
201 [1]
197 $ hg debugsparse
202 $ hg debugsparse
198 [exclude]
203 [exclude]
199 hide*
204 hide*
200
205
201 Temporarily Included Files (for merge/rebase):
206 Temporarily Included Files (for merge/rebase):
202 hide
207 hide
203
208
204 $ hg up -C .
209 $ hg up -C .
205 cleaned up 1 temporarily added file(s) from the sparse checkout
210 cleaned up 1 temporarily added file(s) from the sparse checkout
206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 $ hg debugsparse
212 $ hg debugsparse
208 [exclude]
213 [exclude]
209 hide*
214 hide*
210
215
211
216
212 Verify strip -k resets dirstate correctly
217 Verify strip -k resets dirstate correctly
213
218
214 $ hg status
219 $ hg status
215 $ hg debugsparse
220 $ hg debugsparse
216 [exclude]
221 [exclude]
217 hide*
222 hide*
218
223
219 $ hg log -r . -T '{rev}\n' --stat
224 $ hg log -r . -T '{rev}\n' --stat
220 1
225 1
221 hide | 2 +-
226 hide | 2 +-
222 hide2 | 1 +
227 hide2 | 1 +
223 show | 2 +-
228 show | 2 +-
224 show2 | 1 +
229 show2 | 1 +
225 4 files changed, 4 insertions(+), 2 deletions(-)
230 4 files changed, 4 insertions(+), 2 deletions(-)
226
231
227 $ hg strip -r . -k
232 $ hg strip -r . -k
228 saved backup bundle to $TESTTMP/myrepo/.hg/strip-backup/39278f7c08a9-ce59e002-backup.hg (glob)
233 saved backup bundle to $TESTTMP/myrepo/.hg/strip-backup/39278f7c08a9-ce59e002-backup.hg (glob)
229 $ hg status
234 $ hg status
230 M show
235 M show
231 ? show2
236 ? show2
232
237
233 Verify rebase succeeds if all changed files are in sparse checkout
238 Verify rebase succeeds if all changed files are in sparse checkout
234
239
235 $ hg commit -Aqm "add show2"
240 $ hg commit -Aqm "add show2"
236 $ hg rebase -d 1 --config extensions.rebase=
241 $ hg rebase -d 1 --config extensions.rebase=
237 rebasing 2:bdde55290160 "add show2" (tip)
242 rebasing 2:bdde55290160 "add show2" (tip)
238 saved backup bundle to $TESTTMP/myrepo/.hg/strip-backup/bdde55290160-216ed9c6-rebase.hg (glob)
243 saved backup bundle to $TESTTMP/myrepo/.hg/strip-backup/bdde55290160-216ed9c6-rebase.hg (glob)
239
244
240 Verify log --sparse only shows commits that affect the sparse checkout
245 Verify log --sparse only shows commits that affect the sparse checkout
241
246
242 $ hg log -T '{rev} '
247 $ hg log -T '{rev} '
243 2 1 0 (no-eol)
248 2 1 0 (no-eol)
244 $ hg log --sparse -T '{rev} '
249 $ hg log --sparse -T '{rev} '
245 2 0 (no-eol)
250 2 0 (no-eol)
246
251
247 Test status on a file in a subdir
252 Test status on a file in a subdir
248
253
249 $ mkdir -p dir1/dir2
254 $ mkdir -p dir1/dir2
250 $ touch dir1/dir2/file
255 $ touch dir1/dir2/file
251 $ hg debugsparse -I dir1/dir2
256 $ hg debugsparse -I dir1/dir2
252 $ hg status
257 $ hg status
253 ? dir1/dir2/file
258 ? dir1/dir2/file
254
259
255 Test that add -s adds dirs to sparse profile
260 Test that add -s adds dirs to sparse profile
256
261
257 $ hg debugsparse --reset
262 $ hg debugsparse --reset
258 $ hg debugsparse --include empty
263 $ hg debugsparse --include empty
259 $ hg debugsparse
264 $ hg debugsparse
260 [include]
265 [include]
261 empty
266 empty
262
267
263
268
264 $ mkdir add
269 $ mkdir add
265 $ touch add/foo
270 $ touch add/foo
266 $ touch add/bar
271 $ touch add/bar
267 $ hg add add/foo
272 $ hg add add/foo
268 abort: cannot add 'add/foo' - it is outside the sparse checkout
273 abort: cannot add 'add/foo' - it is outside the sparse checkout
269 (include file with `hg debugsparse --include <pattern>` or use `hg add -s <file>` to include file directory while adding)
274 (include file with `hg debugsparse --include <pattern>` or use `hg add -s <file>` to include file directory while adding)
270 [255]
275 [255]
271 $ hg add -s add/foo
276 $ hg add -s add/foo
272 $ hg st
277 $ hg st
273 A add/foo
278 A add/foo
274 ? add/bar
279 ? add/bar
275 $ hg debugsparse
280 $ hg debugsparse
276 [include]
281 [include]
277 add
282 add
278 empty
283 empty
279
284
280 $ hg add -s add/*
285 $ hg add -s add/*
281 add/foo already tracked!
286 add/foo already tracked!
282 $ hg st
287 $ hg st
283 A add/bar
288 A add/bar
284 A add/foo
289 A add/foo
285 $ hg debugsparse
290 $ hg debugsparse
286 [include]
291 [include]
287 add
292 add
288 empty
293 empty
289
294
290
295
291 $ cd ..
296 $ cd ..
292
297
293 Test non-sparse repos work while sparse is loaded
298 Test non-sparse repos work while sparse is loaded
294 $ hg init sparserepo
299 $ hg init sparserepo
295 $ hg init nonsparserepo
300 $ hg init nonsparserepo
296 $ cd sparserepo
301 $ cd sparserepo
297 $ cat > .hg/hgrc <<EOF
302 $ cat > .hg/hgrc <<EOF
298 > [extensions]
303 > [extensions]
299 > sparse=
304 > sparse=
300 > EOF
305 > EOF
301 $ cd ../nonsparserepo
306 $ cd ../nonsparserepo
302 $ echo x > x && hg add x && hg commit -qAm x
307 $ echo x > x && hg add x && hg commit -qAm x
303 $ cd ../sparserepo
308 $ cd ../sparserepo
304 $ hg clone ../nonsparserepo ../nonsparserepo2
309 $ hg clone ../nonsparserepo ../nonsparserepo2
305 updating to branch default
310 updating to branch default
306 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
307
312
308 Test debugrebuilddirstate
313 Test debugrebuilddirstate
309 $ cd ../sparserepo
314 $ cd ../sparserepo
310 $ touch included
315 $ touch included
311 $ touch excluded
316 $ touch excluded
312 $ hg add included excluded
317 $ hg add included excluded
313 $ hg commit -m 'a commit' -q
318 $ hg commit -m 'a commit' -q
314 $ cp .hg/dirstate ../dirstateboth
319 $ cp .hg/dirstate ../dirstateboth
315 $ hg debugsparse -X excluded
320 $ hg debugsparse -X excluded
316 $ cp ../dirstateboth .hg/dirstate
321 $ cp ../dirstateboth .hg/dirstate
317 $ hg debugrebuilddirstate
322 $ hg debugrebuilddirstate
318 $ hg debugdirstate
323 $ hg debugdirstate
319 n 0 -1 unset included
324 n 0 -1 unset included
320
325
321 Test debugdirstate --minimal where file is in the parent manifest but not the
326 Test debugdirstate --minimal where file is in the parent manifest but not the
322 dirstate
327 dirstate
323 $ hg debugsparse -X included
328 $ hg debugsparse -X included
324 $ hg debugdirstate
329 $ hg debugdirstate
325 $ cp .hg/dirstate ../dirstateallexcluded
330 $ cp .hg/dirstate ../dirstateallexcluded
326 $ hg debugsparse --reset
331 $ hg debugsparse --reset
327 $ hg debugsparse -X excluded
332 $ hg debugsparse -X excluded
328 $ cp ../dirstateallexcluded .hg/dirstate
333 $ cp ../dirstateallexcluded .hg/dirstate
329 $ touch includedadded
334 $ touch includedadded
330 $ hg add includedadded
335 $ hg add includedadded
331 $ hg debugdirstate --nodates
336 $ hg debugdirstate --nodates
332 a 0 -1 unset includedadded
337 a 0 -1 unset includedadded
333 $ hg debugrebuilddirstate --minimal
338 $ hg debugrebuilddirstate --minimal
334 $ hg debugdirstate --nodates
339 $ hg debugdirstate --nodates
335 n 0 -1 unset included
340 n 0 -1 unset included
336 a 0 -1 * includedadded (glob)
341 a 0 -1 * includedadded (glob)
337
342
338 Test debugdirstate --minimal where a file is not in parent manifest
343 Test debugdirstate --minimal where a file is not in parent manifest
339 but in the dirstate. This should take into account excluded files in the
344 but in the dirstate. This should take into account excluded files in the
340 manifest
345 manifest
341 $ cp ../dirstateboth .hg/dirstate
346 $ cp ../dirstateboth .hg/dirstate
342 $ touch includedadded
347 $ touch includedadded
343 $ hg add includedadded
348 $ hg add includedadded
344 $ touch excludednomanifest
349 $ touch excludednomanifest
345 $ hg add excludednomanifest
350 $ hg add excludednomanifest
346 $ cp .hg/dirstate ../moreexcluded
351 $ cp .hg/dirstate ../moreexcluded
347 $ hg forget excludednomanifest
352 $ hg forget excludednomanifest
348 $ rm excludednomanifest
353 $ rm excludednomanifest
349 $ hg debugsparse -X excludednomanifest
354 $ hg debugsparse -X excludednomanifest
350 $ cp ../moreexcluded .hg/dirstate
355 $ cp ../moreexcluded .hg/dirstate
351 $ hg manifest
356 $ hg manifest
352 excluded
357 excluded
353 included
358 included
354 We have files in the dirstate that are included and excluded. Some are in the
359 We have files in the dirstate that are included and excluded. Some are in the
355 manifest and some are not.
360 manifest and some are not.
356 $ hg debugdirstate --nodates
361 $ hg debugdirstate --nodates
357 n 644 0 * excluded (glob)
362 n 644 0 * excluded (glob)
358 a 0 -1 * excludednomanifest (glob)
363 a 0 -1 * excludednomanifest (glob)
359 n 644 0 * included (glob)
364 n 644 0 * included (glob)
360 a 0 -1 * includedadded (glob)
365 a 0 -1 * includedadded (glob)
361 $ hg debugrebuilddirstate --minimal
366 $ hg debugrebuilddirstate --minimal
362 $ hg debugdirstate --nodates
367 $ hg debugdirstate --nodates
363 n 644 0 * included (glob)
368 n 644 0 * included (glob)
364 a 0 -1 * includedadded (glob)
369 a 0 -1 * includedadded (glob)
365
370
General Comments 0
You need to be logged in to leave comments. Login now