##// END OF EJS Templates
sparse: fix debugrebuilddirsate when narrow extension is enabled...
Pulkit Goyal -
r41185:b05eb98a default
parent child Browse files
Show More
@@ -1,347 +1,347 b''
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]``.
38 have ``[include]`` after ``[exclude]``.
39
39
40 Non-special lines resemble file patterns to be added to either includes
40 Non-special lines resemble file patterns to be added to either includes
41 or excludes. The syntax of these lines is documented by :hg:`help patterns`.
41 or excludes. The syntax of these lines is documented by :hg:`help patterns`.
42 Patterns are interpreted as ``glob:`` by default and match against the
42 Patterns are interpreted as ``glob:`` by default and match against the
43 root of the repository.
43 root of the repository.
44
44
45 Exclusion patterns take precedence over inclusion patterns. So even
45 Exclusion patterns take precedence over inclusion patterns. So even
46 if a file is explicitly included, an ``[exclude]`` entry can remove it.
46 if a file is explicitly included, an ``[exclude]`` entry can remove it.
47
47
48 For example, say you have a repository with 3 directories, ``frontend/``,
48 For example, say you have a repository with 3 directories, ``frontend/``,
49 ``backend/``, and ``tools/``. ``frontend/`` and ``backend/`` correspond
49 ``backend/``, and ``tools/``. ``frontend/`` and ``backend/`` correspond
50 to different projects and it is uncommon for someone working on one
50 to different projects and it is uncommon for someone working on one
51 to need the files for the other. But ``tools/`` contains files shared
51 to need the files for the other. But ``tools/`` contains files shared
52 between both projects. Your sparse config files may resemble::
52 between both projects. Your sparse config files may resemble::
53
53
54 # frontend.sparse
54 # frontend.sparse
55 frontend/**
55 frontend/**
56 tools/**
56 tools/**
57
57
58 # backend.sparse
58 # backend.sparse
59 backend/**
59 backend/**
60 tools/**
60 tools/**
61
61
62 Say the backend grows in size. Or there's a directory with thousands
62 Say the backend grows in size. Or there's a directory with thousands
63 of files you wish to exclude. You can modify the profile to exclude
63 of files you wish to exclude. You can modify the profile to exclude
64 certain files::
64 certain files::
65
65
66 [include]
66 [include]
67 backend/**
67 backend/**
68 tools/**
68 tools/**
69
69
70 [exclude]
70 [exclude]
71 tools/tests/**
71 tools/tests/**
72 """
72 """
73
73
74 from __future__ import absolute_import
74 from __future__ import absolute_import
75
75
76 from mercurial.i18n import _
76 from mercurial.i18n import _
77 from mercurial import (
77 from mercurial import (
78 commands,
78 commands,
79 dirstate,
79 dirstate,
80 error,
80 error,
81 extensions,
81 extensions,
82 hg,
82 hg,
83 logcmdutil,
83 logcmdutil,
84 match as matchmod,
84 match as matchmod,
85 pycompat,
85 pycompat,
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 _initialrevs(orig, repo, opts):
129 def _initialrevs(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(logcmdutil, '_initialrevs', _initialrevs)
138 extensions.wrapfunction(logcmdutil, '_initialrevs', _initialrevs)
139
139
140 def _clonesparsecmd(orig, ui, repo, *args, **opts):
140 def _clonesparsecmd(orig, ui, repo, *args, **opts):
141 include_pat = opts.get(r'include')
141 include_pat = opts.get(r'include')
142 exclude_pat = opts.get(r'exclude')
142 exclude_pat = opts.get(r'exclude')
143 enableprofile_pat = opts.get(r'enable_profile')
143 enableprofile_pat = opts.get(r'enable_profile')
144 narrow_pat = opts.get(r'narrow')
144 narrow_pat = opts.get(r'narrow')
145 include = exclude = enableprofile = False
145 include = exclude = enableprofile = False
146 if include_pat:
146 if include_pat:
147 pat = include_pat
147 pat = include_pat
148 include = True
148 include = True
149 if exclude_pat:
149 if exclude_pat:
150 pat = exclude_pat
150 pat = exclude_pat
151 exclude = True
151 exclude = True
152 if enableprofile_pat:
152 if enableprofile_pat:
153 pat = enableprofile_pat
153 pat = enableprofile_pat
154 enableprofile = True
154 enableprofile = True
155 if sum([include, exclude, enableprofile]) > 1:
155 if sum([include, exclude, enableprofile]) > 1:
156 raise error.Abort(_("too many flags specified."))
156 raise error.Abort(_("too many flags specified."))
157 # if --narrow is passed, it means they are includes and excludes for narrow
157 # if --narrow is passed, it means they are includes and excludes for narrow
158 # clone
158 # clone
159 if not narrow_pat and (include or exclude or enableprofile):
159 if not narrow_pat and (include or exclude or enableprofile):
160 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
160 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
161 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
161 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
162 exclude=exclude, enableprofile=enableprofile,
162 exclude=exclude, enableprofile=enableprofile,
163 usereporootpaths=True)
163 usereporootpaths=True)
164 return orig(self, node, overwrite, *args, **kwargs)
164 return orig(self, node, overwrite, *args, **kwargs)
165 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
165 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
166 return orig(ui, repo, *args, **opts)
166 return orig(ui, repo, *args, **opts)
167
167
168 def _setupclone(ui):
168 def _setupclone(ui):
169 entry = commands.table['clone']
169 entry = commands.table['clone']
170 entry[1].append(('', 'enable-profile', [],
170 entry[1].append(('', 'enable-profile', [],
171 'enable a sparse profile'))
171 'enable a sparse profile'))
172 entry[1].append(('', 'include', [],
172 entry[1].append(('', 'include', [],
173 'include sparse pattern'))
173 'include sparse pattern'))
174 entry[1].append(('', 'exclude', [],
174 entry[1].append(('', 'exclude', [],
175 'exclude sparse pattern'))
175 'exclude sparse pattern'))
176 extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
176 extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
177
177
178 def _setupadd(ui):
178 def _setupadd(ui):
179 entry = commands.table['add']
179 entry = commands.table['add']
180 entry[1].append(('s', 'sparse', None,
180 entry[1].append(('s', 'sparse', None,
181 'also include directories of added files in sparse config'))
181 'also include directories of added files in sparse config'))
182
182
183 def _add(orig, ui, repo, *pats, **opts):
183 def _add(orig, ui, repo, *pats, **opts):
184 if opts.get(r'sparse'):
184 if opts.get(r'sparse'):
185 dirs = set()
185 dirs = set()
186 for pat in pats:
186 for pat in pats:
187 dirname, basename = util.split(pat)
187 dirname, basename = util.split(pat)
188 dirs.add(dirname)
188 dirs.add(dirname)
189 sparse.updateconfig(repo, list(dirs), opts, include=True)
189 sparse.updateconfig(repo, list(dirs), opts, include=True)
190 return orig(ui, repo, *pats, **opts)
190 return orig(ui, repo, *pats, **opts)
191
191
192 extensions.wrapcommand(commands.table, 'add', _add)
192 extensions.wrapcommand(commands.table, 'add', _add)
193
193
194 def _setupdirstate(ui):
194 def _setupdirstate(ui):
195 """Modify the dirstate to prevent stat'ing excluded files,
195 """Modify the dirstate to prevent stat'ing excluded files,
196 and to prevent modifications to files outside the checkout.
196 and to prevent modifications to files outside the checkout.
197 """
197 """
198
198
199 def walk(orig, self, match, subrepos, unknown, ignored, full=True):
199 def walk(orig, self, match, subrepos, unknown, ignored, full=True):
200 # hack to not exclude explicitly-specified paths so that they can
200 # hack to not exclude explicitly-specified paths so that they can
201 # be warned later on e.g. dirstate.add()
201 # be warned later on e.g. dirstate.add()
202 em = matchmod.exact(match._root, match._cwd, match.files())
202 em = matchmod.exact(match._root, match._cwd, match.files())
203 sm = matchmod.unionmatcher([self._sparsematcher, em])
203 sm = matchmod.unionmatcher([self._sparsematcher, em])
204 match = matchmod.intersectmatchers(match, sm)
204 match = matchmod.intersectmatchers(match, sm)
205 return orig(self, match, subrepos, unknown, ignored, full)
205 return orig(self, match, subrepos, unknown, ignored, full)
206
206
207 extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
207 extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
208
208
209 # dirstate.rebuild should not add non-matching files
209 # dirstate.rebuild should not add non-matching files
210 def _rebuild(orig, self, parent, allfiles, changedfiles=None):
210 def _rebuild(orig, self, parent, allfiles, changedfiles=None):
211 matcher = self._sparsematcher
211 matcher = self._sparsematcher
212 if not matcher.always():
212 if not matcher.always():
213 allfiles = allfiles.matches(matcher)
213 allfiles = [f for f in allfiles if matcher(f)]
214 if changedfiles:
214 if changedfiles:
215 changedfiles = [f for f in changedfiles if matcher(f)]
215 changedfiles = [f for f in changedfiles if matcher(f)]
216
216
217 if changedfiles is not None:
217 if changedfiles is not None:
218 # In _rebuild, these files will be deleted from the dirstate
218 # In _rebuild, these files will be deleted from the dirstate
219 # when they are not found to be in allfiles
219 # when they are not found to be in allfiles
220 dirstatefilestoremove = set(f for f in self if not matcher(f))
220 dirstatefilestoremove = set(f for f in self if not matcher(f))
221 changedfiles = dirstatefilestoremove.union(changedfiles)
221 changedfiles = dirstatefilestoremove.union(changedfiles)
222
222
223 return orig(self, parent, allfiles, changedfiles)
223 return orig(self, parent, allfiles, changedfiles)
224 extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
224 extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
225
225
226 # Prevent adding files that are outside the sparse checkout
226 # Prevent adding files that are outside the sparse checkout
227 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
227 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
228 hint = _('include file with `hg debugsparse --include <pattern>` or use ' +
228 hint = _('include file with `hg debugsparse --include <pattern>` or use ' +
229 '`hg add -s <file>` to include file directory while adding')
229 '`hg add -s <file>` to include file directory while adding')
230 for func in editfuncs:
230 for func in editfuncs:
231 def _wrapper(orig, self, *args):
231 def _wrapper(orig, self, *args):
232 sparsematch = self._sparsematcher
232 sparsematch = self._sparsematcher
233 if not sparsematch.always():
233 if not sparsematch.always():
234 for f in args:
234 for f in args:
235 if (f is not None and not sparsematch(f) and
235 if (f is not None and not sparsematch(f) and
236 f not in self):
236 f not in self):
237 raise error.Abort(_("cannot add '%s' - it is outside "
237 raise error.Abort(_("cannot add '%s' - it is outside "
238 "the sparse checkout") % f,
238 "the sparse checkout") % f,
239 hint=hint)
239 hint=hint)
240 return orig(self, *args)
240 return orig(self, *args)
241 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
241 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
242
242
243 @command('debugsparse', [
243 @command('debugsparse', [
244 ('I', 'include', False, _('include files in the sparse checkout')),
244 ('I', 'include', False, _('include files in the sparse checkout')),
245 ('X', 'exclude', False, _('exclude files in the sparse checkout')),
245 ('X', 'exclude', False, _('exclude files in the sparse checkout')),
246 ('d', 'delete', False, _('delete an include/exclude rule')),
246 ('d', 'delete', False, _('delete an include/exclude rule')),
247 ('f', 'force', False, _('allow changing rules even with pending changes')),
247 ('f', 'force', False, _('allow changing rules even with pending changes')),
248 ('', 'enable-profile', False, _('enables the specified profile')),
248 ('', 'enable-profile', False, _('enables the specified profile')),
249 ('', 'disable-profile', False, _('disables the specified profile')),
249 ('', 'disable-profile', False, _('disables the specified profile')),
250 ('', 'import-rules', False, _('imports rules from a file')),
250 ('', 'import-rules', False, _('imports rules from a file')),
251 ('', 'clear-rules', False, _('clears local include/exclude rules')),
251 ('', 'clear-rules', False, _('clears local include/exclude rules')),
252 ('', 'refresh', False, _('updates the working after sparseness changes')),
252 ('', 'refresh', False, _('updates the working after sparseness changes')),
253 ('', 'reset', False, _('makes the repo full again')),
253 ('', 'reset', False, _('makes the repo full again')),
254 ] + commands.templateopts,
254 ] + commands.templateopts,
255 _('[--OPTION] PATTERN...'),
255 _('[--OPTION] PATTERN...'),
256 helpbasic=True)
256 helpbasic=True)
257 def debugsparse(ui, repo, *pats, **opts):
257 def debugsparse(ui, repo, *pats, **opts):
258 """make the current checkout sparse, or edit the existing checkout
258 """make the current checkout sparse, or edit the existing checkout
259
259
260 The sparse command is used to make the current checkout sparse.
260 The sparse command is used to make the current checkout sparse.
261 This means files that don't meet the sparse condition will not be
261 This means files that don't meet the sparse condition will not be
262 written to disk, or show up in any working copy operations. It does
262 written to disk, or show up in any working copy operations. It does
263 not affect files in history in any way.
263 not affect files in history in any way.
264
264
265 Passing no arguments prints the currently applied sparse rules.
265 Passing no arguments prints the currently applied sparse rules.
266
266
267 --include and --exclude are used to add and remove files from the sparse
267 --include and --exclude are used to add and remove files from the sparse
268 checkout. The effects of adding an include or exclude rule are applied
268 checkout. The effects of adding an include or exclude rule are applied
269 immediately. If applying the new rule would cause a file with pending
269 immediately. If applying the new rule would cause a file with pending
270 changes to be added or removed, the command will fail. Pass --force to
270 changes to be added or removed, the command will fail. Pass --force to
271 force a rule change even with pending changes (the changes on disk will
271 force a rule change even with pending changes (the changes on disk will
272 be preserved).
272 be preserved).
273
273
274 --delete removes an existing include/exclude rule. The effects are
274 --delete removes an existing include/exclude rule. The effects are
275 immediate.
275 immediate.
276
276
277 --refresh refreshes the files on disk based on the sparse rules. This is
277 --refresh refreshes the files on disk based on the sparse rules. This is
278 only necessary if .hg/sparse was changed by hand.
278 only necessary if .hg/sparse was changed by hand.
279
279
280 --enable-profile and --disable-profile accept a path to a .hgsparse file.
280 --enable-profile and --disable-profile accept a path to a .hgsparse file.
281 This allows defining sparse checkouts and tracking them inside the
281 This allows defining sparse checkouts and tracking them inside the
282 repository. This is useful for defining commonly used sparse checkouts for
282 repository. This is useful for defining commonly used sparse checkouts for
283 many people to use. As the profile definition changes over time, the sparse
283 many people to use. As the profile definition changes over time, the sparse
284 checkout will automatically be updated appropriately, depending on which
284 checkout will automatically be updated appropriately, depending on which
285 changeset is checked out. Changes to .hgsparse are not applied until they
285 changeset is checked out. Changes to .hgsparse are not applied until they
286 have been committed.
286 have been committed.
287
287
288 --import-rules accepts a path to a file containing rules in the .hgsparse
288 --import-rules accepts a path to a file containing rules in the .hgsparse
289 format, allowing you to add --include, --exclude and --enable-profile rules
289 format, allowing you to add --include, --exclude and --enable-profile rules
290 in bulk. Like the --include, --exclude and --enable-profile switches, the
290 in bulk. Like the --include, --exclude and --enable-profile switches, the
291 changes are applied immediately.
291 changes are applied immediately.
292
292
293 --clear-rules removes all local include and exclude rules, while leaving
293 --clear-rules removes all local include and exclude rules, while leaving
294 any enabled profiles in place.
294 any enabled profiles in place.
295
295
296 Returns 0 if editing the sparse checkout succeeds.
296 Returns 0 if editing the sparse checkout succeeds.
297 """
297 """
298 opts = pycompat.byteskwargs(opts)
298 opts = pycompat.byteskwargs(opts)
299 include = opts.get('include')
299 include = opts.get('include')
300 exclude = opts.get('exclude')
300 exclude = opts.get('exclude')
301 force = opts.get('force')
301 force = opts.get('force')
302 enableprofile = opts.get('enable_profile')
302 enableprofile = opts.get('enable_profile')
303 disableprofile = opts.get('disable_profile')
303 disableprofile = opts.get('disable_profile')
304 importrules = opts.get('import_rules')
304 importrules = opts.get('import_rules')
305 clearrules = opts.get('clear_rules')
305 clearrules = opts.get('clear_rules')
306 delete = opts.get('delete')
306 delete = opts.get('delete')
307 refresh = opts.get('refresh')
307 refresh = opts.get('refresh')
308 reset = opts.get('reset')
308 reset = opts.get('reset')
309 count = sum([include, exclude, enableprofile, disableprofile, delete,
309 count = sum([include, exclude, enableprofile, disableprofile, delete,
310 importrules, refresh, clearrules, reset])
310 importrules, refresh, clearrules, reset])
311 if count > 1:
311 if count > 1:
312 raise error.Abort(_("too many flags specified"))
312 raise error.Abort(_("too many flags specified"))
313
313
314 if count == 0:
314 if count == 0:
315 if repo.vfs.exists('sparse'):
315 if repo.vfs.exists('sparse'):
316 ui.status(repo.vfs.read("sparse") + "\n")
316 ui.status(repo.vfs.read("sparse") + "\n")
317 temporaryincludes = sparse.readtemporaryincludes(repo)
317 temporaryincludes = sparse.readtemporaryincludes(repo)
318 if temporaryincludes:
318 if temporaryincludes:
319 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
319 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
320 ui.status(("\n".join(temporaryincludes) + "\n"))
320 ui.status(("\n".join(temporaryincludes) + "\n"))
321 else:
321 else:
322 ui.status(_('repo is not sparse\n'))
322 ui.status(_('repo is not sparse\n'))
323 return
323 return
324
324
325 if include or exclude or delete or reset or enableprofile or disableprofile:
325 if include or exclude or delete or reset or enableprofile or disableprofile:
326 sparse.updateconfig(repo, pats, opts, include=include, exclude=exclude,
326 sparse.updateconfig(repo, pats, opts, include=include, exclude=exclude,
327 reset=reset, delete=delete,
327 reset=reset, delete=delete,
328 enableprofile=enableprofile,
328 enableprofile=enableprofile,
329 disableprofile=disableprofile, force=force)
329 disableprofile=disableprofile, force=force)
330
330
331 if importrules:
331 if importrules:
332 sparse.importfromfiles(repo, opts, pats, force=force)
332 sparse.importfromfiles(repo, opts, pats, force=force)
333
333
334 if clearrules:
334 if clearrules:
335 sparse.clearrules(repo, force=force)
335 sparse.clearrules(repo, force=force)
336
336
337 if refresh:
337 if refresh:
338 try:
338 try:
339 wlock = repo.wlock()
339 wlock = repo.wlock()
340 fcounts = map(
340 fcounts = map(
341 len,
341 len,
342 sparse.refreshwdir(repo, repo.status(), sparse.matcher(repo),
342 sparse.refreshwdir(repo, repo.status(), sparse.matcher(repo),
343 force=force))
343 force=force))
344 sparse.printchanges(ui, opts, added=fcounts[0], dropped=fcounts[1],
344 sparse.printchanges(ui, opts, added=fcounts[0], dropped=fcounts[1],
345 conflicting=fcounts[2])
345 conflicting=fcounts[2])
346 finally:
346 finally:
347 wlock.release()
347 wlock.release()
@@ -1,109 +1,69 b''
1 Testing interaction of sparse and narrow when both are enabled on the client
1 Testing interaction of sparse and narrow when both are enabled on the client
2 side and we do a non-ellipsis clone
2 side and we do a non-ellipsis clone
3
3
4 #testcases tree flat
4 #testcases tree flat
5 $ . "$TESTDIR/narrow-library.sh"
5 $ . "$TESTDIR/narrow-library.sh"
6 $ cat << EOF >> $HGRCPATH
6 $ cat << EOF >> $HGRCPATH
7 > [extensions]
7 > [extensions]
8 > sparse =
8 > sparse =
9 > EOF
9 > EOF
10
10
11 #if tree
11 #if tree
12 $ cat << EOF >> $HGRCPATH
12 $ cat << EOF >> $HGRCPATH
13 > [experimental]
13 > [experimental]
14 > treemanifest = 1
14 > treemanifest = 1
15 > EOF
15 > EOF
16 #endif
16 #endif
17
17
18 $ hg init master
18 $ hg init master
19 $ cd master
19 $ cd master
20
20
21 $ mkdir inside
21 $ mkdir inside
22 $ echo 'inside' > inside/f
22 $ echo 'inside' > inside/f
23 $ hg add inside/f
23 $ hg add inside/f
24 $ hg commit -m 'add inside'
24 $ hg commit -m 'add inside'
25
25
26 $ mkdir widest
26 $ mkdir widest
27 $ echo 'widest' > widest/f
27 $ echo 'widest' > widest/f
28 $ hg add widest/f
28 $ hg add widest/f
29 $ hg commit -m 'add widest'
29 $ hg commit -m 'add widest'
30
30
31 $ mkdir outside
31 $ mkdir outside
32 $ echo 'outside' > outside/f
32 $ echo 'outside' > outside/f
33 $ hg add outside/f
33 $ hg add outside/f
34 $ hg commit -m 'add outside'
34 $ hg commit -m 'add outside'
35
35
36 $ cd ..
36 $ cd ..
37
37
38 narrow clone the inside file
38 narrow clone the inside file
39
39
40 $ hg clone --narrow ssh://user@dummy/master narrow --include inside/f
40 $ hg clone --narrow ssh://user@dummy/master narrow --include inside/f
41 requesting all changes
41 requesting all changes
42 adding changesets
42 adding changesets
43 adding manifests
43 adding manifests
44 adding file changes
44 adding file changes
45 added 3 changesets with 1 changes to 1 files
45 added 3 changesets with 1 changes to 1 files
46 new changesets *:* (glob)
46 new changesets *:* (glob)
47 updating to branch default
47 updating to branch default
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 $ cd narrow
49 $ cd narrow
50 $ hg tracked
50 $ hg tracked
51 I path:inside/f
51 I path:inside/f
52 $ hg files
52 $ hg files
53 inside/f
53 inside/f
54
54
55 XXX: we should have a flag in `hg debugsparse` to list the sparse profile
55 XXX: we should have a flag in `hg debugsparse` to list the sparse profile
56 $ test -f .hg/sparse
56 $ test -f .hg/sparse
57 [1]
57 [1]
58
58
59 $ cat .hg/requires
59 $ cat .hg/requires
60 dotencode
60 dotencode
61 fncache
61 fncache
62 generaldelta
62 generaldelta
63 narrowhg-experimental
63 narrowhg-experimental
64 revlogv1
64 revlogv1
65 sparserevlog
65 sparserevlog
66 store
66 store
67 treemanifest (tree !)
67 treemanifest (tree !)
68
68
69 $ hg debugrebuilddirstate
69 $ hg debugrebuilddirstate
70 ** unknown exception encountered, please report by visiting
71 ** https://mercurial-scm.org/wiki/BugTracker
72 ** Python 2.7.12 (default, Nov 12 2018, 14:36:49) [GCC 5.4.0 20160609]
73 ** Mercurial Distributed SCM (version 4.8.1+588-479a5ea51ccc+20181224)
74 ** Extensions loaded: narrow, sparse
75 Traceback (most recent call last):
76 File "/place/vartmp/hgtests.zMelCK/install/bin/hg", line 43, in <module>
77 dispatch.run()
78 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 99, in run
79 status = dispatch(req)
80 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 225, in dispatch
81 ret = _runcatch(req) or 0
82 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 376, in _runcatch
83 return _callcatch(ui, _runcatchfunc)
84 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 384, in _callcatch
85 return scmutil.callcatch(ui, func)
86 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/scmutil.py", line 166, in callcatch
87 return func()
88 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 367, in _runcatchfunc
89 return _dispatch(req)
90 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 1021, in _dispatch
91 cmdpats, cmdoptions)
92 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 756, in runcommand
93 ret = _runcommand(ui, options, cmd, d)
94 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 1030, in _runcommand
95 return cmdfunc()
96 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/dispatch.py", line 1018, in <lambda>
97 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
98 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/util.py", line 1670, in check
99 return func(*args, **kwargs)
100 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/debugcommands.py", line 1998, in debugrebuilddirstate
101 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
102 File "/place/vartmp/hgtests.zMelCK/install/lib/python/hgext/narrow/narrowdirstate.py", line 60, in rebuild
103 super(narrowdirstate, self).rebuild(parent, allfiles, changedfiles)
104 File "/place/vartmp/hgtests.zMelCK/install/lib/python/mercurial/extensions.py", line 437, in closure
105 return func(*(args + a), **kw)
106 File "/place/vartmp/hgtests.zMelCK/install/lib/python/hgext/sparse.py", line 213, in _rebuild
107 allfiles = allfiles.matches(matcher)
108 AttributeError: 'list' object has no attribute 'matches'
109 [1]
General Comments 0
You need to be logged in to leave comments. Login now