##// END OF EJS Templates
sparse: don't enable on clone if it was a narrow clone...
Pulkit Goyal -
r41183:8eaf693b default
parent child Browse files
Show More
@@ -1,344 +1,347
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 include = exclude = enableprofile = False
145 include = exclude = enableprofile = False
145 if include_pat:
146 if include_pat:
146 pat = include_pat
147 pat = include_pat
147 include = True
148 include = True
148 if exclude_pat:
149 if exclude_pat:
149 pat = exclude_pat
150 pat = exclude_pat
150 exclude = True
151 exclude = True
151 if enableprofile_pat:
152 if enableprofile_pat:
152 pat = enableprofile_pat
153 pat = enableprofile_pat
153 enableprofile = True
154 enableprofile = True
154 if sum([include, exclude, enableprofile]) > 1:
155 if sum([include, exclude, enableprofile]) > 1:
155 raise error.Abort(_("too many flags specified."))
156 raise error.Abort(_("too many flags specified."))
156 if include or exclude or enableprofile:
157 # if --narrow is passed, it means they are includes and excludes for narrow
158 # clone
159 if not narrow_pat and (include or exclude or enableprofile):
157 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
160 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
158 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
161 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
159 exclude=exclude, enableprofile=enableprofile,
162 exclude=exclude, enableprofile=enableprofile,
160 usereporootpaths=True)
163 usereporootpaths=True)
161 return orig(self, node, overwrite, *args, **kwargs)
164 return orig(self, node, overwrite, *args, **kwargs)
162 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
165 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
163 return orig(ui, repo, *args, **opts)
166 return orig(ui, repo, *args, **opts)
164
167
165 def _setupclone(ui):
168 def _setupclone(ui):
166 entry = commands.table['clone']
169 entry = commands.table['clone']
167 entry[1].append(('', 'enable-profile', [],
170 entry[1].append(('', 'enable-profile', [],
168 'enable a sparse profile'))
171 'enable a sparse profile'))
169 entry[1].append(('', 'include', [],
172 entry[1].append(('', 'include', [],
170 'include sparse pattern'))
173 'include sparse pattern'))
171 entry[1].append(('', 'exclude', [],
174 entry[1].append(('', 'exclude', [],
172 'exclude sparse pattern'))
175 'exclude sparse pattern'))
173 extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
176 extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
174
177
175 def _setupadd(ui):
178 def _setupadd(ui):
176 entry = commands.table['add']
179 entry = commands.table['add']
177 entry[1].append(('s', 'sparse', None,
180 entry[1].append(('s', 'sparse', None,
178 'also include directories of added files in sparse config'))
181 'also include directories of added files in sparse config'))
179
182
180 def _add(orig, ui, repo, *pats, **opts):
183 def _add(orig, ui, repo, *pats, **opts):
181 if opts.get(r'sparse'):
184 if opts.get(r'sparse'):
182 dirs = set()
185 dirs = set()
183 for pat in pats:
186 for pat in pats:
184 dirname, basename = util.split(pat)
187 dirname, basename = util.split(pat)
185 dirs.add(dirname)
188 dirs.add(dirname)
186 sparse.updateconfig(repo, list(dirs), opts, include=True)
189 sparse.updateconfig(repo, list(dirs), opts, include=True)
187 return orig(ui, repo, *pats, **opts)
190 return orig(ui, repo, *pats, **opts)
188
191
189 extensions.wrapcommand(commands.table, 'add', _add)
192 extensions.wrapcommand(commands.table, 'add', _add)
190
193
191 def _setupdirstate(ui):
194 def _setupdirstate(ui):
192 """Modify the dirstate to prevent stat'ing excluded files,
195 """Modify the dirstate to prevent stat'ing excluded files,
193 and to prevent modifications to files outside the checkout.
196 and to prevent modifications to files outside the checkout.
194 """
197 """
195
198
196 def walk(orig, self, match, subrepos, unknown, ignored, full=True):
199 def walk(orig, self, match, subrepos, unknown, ignored, full=True):
197 # hack to not exclude explicitly-specified paths so that they can
200 # hack to not exclude explicitly-specified paths so that they can
198 # be warned later on e.g. dirstate.add()
201 # be warned later on e.g. dirstate.add()
199 em = matchmod.exact(match._root, match._cwd, match.files())
202 em = matchmod.exact(match._root, match._cwd, match.files())
200 sm = matchmod.unionmatcher([self._sparsematcher, em])
203 sm = matchmod.unionmatcher([self._sparsematcher, em])
201 match = matchmod.intersectmatchers(match, sm)
204 match = matchmod.intersectmatchers(match, sm)
202 return orig(self, match, subrepos, unknown, ignored, full)
205 return orig(self, match, subrepos, unknown, ignored, full)
203
206
204 extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
207 extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
205
208
206 # dirstate.rebuild should not add non-matching files
209 # dirstate.rebuild should not add non-matching files
207 def _rebuild(orig, self, parent, allfiles, changedfiles=None):
210 def _rebuild(orig, self, parent, allfiles, changedfiles=None):
208 matcher = self._sparsematcher
211 matcher = self._sparsematcher
209 if not matcher.always():
212 if not matcher.always():
210 allfiles = allfiles.matches(matcher)
213 allfiles = allfiles.matches(matcher)
211 if changedfiles:
214 if changedfiles:
212 changedfiles = [f for f in changedfiles if matcher(f)]
215 changedfiles = [f for f in changedfiles if matcher(f)]
213
216
214 if changedfiles is not None:
217 if changedfiles is not None:
215 # In _rebuild, these files will be deleted from the dirstate
218 # In _rebuild, these files will be deleted from the dirstate
216 # when they are not found to be in allfiles
219 # when they are not found to be in allfiles
217 dirstatefilestoremove = set(f for f in self if not matcher(f))
220 dirstatefilestoremove = set(f for f in self if not matcher(f))
218 changedfiles = dirstatefilestoremove.union(changedfiles)
221 changedfiles = dirstatefilestoremove.union(changedfiles)
219
222
220 return orig(self, parent, allfiles, changedfiles)
223 return orig(self, parent, allfiles, changedfiles)
221 extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
224 extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
222
225
223 # Prevent adding files that are outside the sparse checkout
226 # Prevent adding files that are outside the sparse checkout
224 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
227 editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
225 hint = _('include file with `hg debugsparse --include <pattern>` or use ' +
228 hint = _('include file with `hg debugsparse --include <pattern>` or use ' +
226 '`hg add -s <file>` to include file directory while adding')
229 '`hg add -s <file>` to include file directory while adding')
227 for func in editfuncs:
230 for func in editfuncs:
228 def _wrapper(orig, self, *args):
231 def _wrapper(orig, self, *args):
229 sparsematch = self._sparsematcher
232 sparsematch = self._sparsematcher
230 if not sparsematch.always():
233 if not sparsematch.always():
231 for f in args:
234 for f in args:
232 if (f is not None and not sparsematch(f) and
235 if (f is not None and not sparsematch(f) and
233 f not in self):
236 f not in self):
234 raise error.Abort(_("cannot add '%s' - it is outside "
237 raise error.Abort(_("cannot add '%s' - it is outside "
235 "the sparse checkout") % f,
238 "the sparse checkout") % f,
236 hint=hint)
239 hint=hint)
237 return orig(self, *args)
240 return orig(self, *args)
238 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
241 extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
239
242
240 @command('debugsparse', [
243 @command('debugsparse', [
241 ('I', 'include', False, _('include files in the sparse checkout')),
244 ('I', 'include', False, _('include files in the sparse checkout')),
242 ('X', 'exclude', False, _('exclude files in the sparse checkout')),
245 ('X', 'exclude', False, _('exclude files in the sparse checkout')),
243 ('d', 'delete', False, _('delete an include/exclude rule')),
246 ('d', 'delete', False, _('delete an include/exclude rule')),
244 ('f', 'force', False, _('allow changing rules even with pending changes')),
247 ('f', 'force', False, _('allow changing rules even with pending changes')),
245 ('', 'enable-profile', False, _('enables the specified profile')),
248 ('', 'enable-profile', False, _('enables the specified profile')),
246 ('', 'disable-profile', False, _('disables the specified profile')),
249 ('', 'disable-profile', False, _('disables the specified profile')),
247 ('', 'import-rules', False, _('imports rules from a file')),
250 ('', 'import-rules', False, _('imports rules from a file')),
248 ('', 'clear-rules', False, _('clears local include/exclude rules')),
251 ('', 'clear-rules', False, _('clears local include/exclude rules')),
249 ('', 'refresh', False, _('updates the working after sparseness changes')),
252 ('', 'refresh', False, _('updates the working after sparseness changes')),
250 ('', 'reset', False, _('makes the repo full again')),
253 ('', 'reset', False, _('makes the repo full again')),
251 ] + commands.templateopts,
254 ] + commands.templateopts,
252 _('[--OPTION] PATTERN...'),
255 _('[--OPTION] PATTERN...'),
253 helpbasic=True)
256 helpbasic=True)
254 def debugsparse(ui, repo, *pats, **opts):
257 def debugsparse(ui, repo, *pats, **opts):
255 """make the current checkout sparse, or edit the existing checkout
258 """make the current checkout sparse, or edit the existing checkout
256
259
257 The sparse command is used to make the current checkout sparse.
260 The sparse command is used to make the current checkout sparse.
258 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
259 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
260 not affect files in history in any way.
263 not affect files in history in any way.
261
264
262 Passing no arguments prints the currently applied sparse rules.
265 Passing no arguments prints the currently applied sparse rules.
263
266
264 --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
265 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
266 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
267 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
268 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
269 be preserved).
272 be preserved).
270
273
271 --delete removes an existing include/exclude rule. The effects are
274 --delete removes an existing include/exclude rule. The effects are
272 immediate.
275 immediate.
273
276
274 --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
275 only necessary if .hg/sparse was changed by hand.
278 only necessary if .hg/sparse was changed by hand.
276
279
277 --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.
278 This allows defining sparse checkouts and tracking them inside the
281 This allows defining sparse checkouts and tracking them inside the
279 repository. This is useful for defining commonly used sparse checkouts for
282 repository. This is useful for defining commonly used sparse checkouts for
280 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
281 checkout will automatically be updated appropriately, depending on which
284 checkout will automatically be updated appropriately, depending on which
282 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
283 have been committed.
286 have been committed.
284
287
285 --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
286 format, allowing you to add --include, --exclude and --enable-profile rules
289 format, allowing you to add --include, --exclude and --enable-profile rules
287 in bulk. Like the --include, --exclude and --enable-profile switches, the
290 in bulk. Like the --include, --exclude and --enable-profile switches, the
288 changes are applied immediately.
291 changes are applied immediately.
289
292
290 --clear-rules removes all local include and exclude rules, while leaving
293 --clear-rules removes all local include and exclude rules, while leaving
291 any enabled profiles in place.
294 any enabled profiles in place.
292
295
293 Returns 0 if editing the sparse checkout succeeds.
296 Returns 0 if editing the sparse checkout succeeds.
294 """
297 """
295 opts = pycompat.byteskwargs(opts)
298 opts = pycompat.byteskwargs(opts)
296 include = opts.get('include')
299 include = opts.get('include')
297 exclude = opts.get('exclude')
300 exclude = opts.get('exclude')
298 force = opts.get('force')
301 force = opts.get('force')
299 enableprofile = opts.get('enable_profile')
302 enableprofile = opts.get('enable_profile')
300 disableprofile = opts.get('disable_profile')
303 disableprofile = opts.get('disable_profile')
301 importrules = opts.get('import_rules')
304 importrules = opts.get('import_rules')
302 clearrules = opts.get('clear_rules')
305 clearrules = opts.get('clear_rules')
303 delete = opts.get('delete')
306 delete = opts.get('delete')
304 refresh = opts.get('refresh')
307 refresh = opts.get('refresh')
305 reset = opts.get('reset')
308 reset = opts.get('reset')
306 count = sum([include, exclude, enableprofile, disableprofile, delete,
309 count = sum([include, exclude, enableprofile, disableprofile, delete,
307 importrules, refresh, clearrules, reset])
310 importrules, refresh, clearrules, reset])
308 if count > 1:
311 if count > 1:
309 raise error.Abort(_("too many flags specified"))
312 raise error.Abort(_("too many flags specified"))
310
313
311 if count == 0:
314 if count == 0:
312 if repo.vfs.exists('sparse'):
315 if repo.vfs.exists('sparse'):
313 ui.status(repo.vfs.read("sparse") + "\n")
316 ui.status(repo.vfs.read("sparse") + "\n")
314 temporaryincludes = sparse.readtemporaryincludes(repo)
317 temporaryincludes = sparse.readtemporaryincludes(repo)
315 if temporaryincludes:
318 if temporaryincludes:
316 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
319 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
317 ui.status(("\n".join(temporaryincludes) + "\n"))
320 ui.status(("\n".join(temporaryincludes) + "\n"))
318 else:
321 else:
319 ui.status(_('repo is not sparse\n'))
322 ui.status(_('repo is not sparse\n'))
320 return
323 return
321
324
322 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:
323 sparse.updateconfig(repo, pats, opts, include=include, exclude=exclude,
326 sparse.updateconfig(repo, pats, opts, include=include, exclude=exclude,
324 reset=reset, delete=delete,
327 reset=reset, delete=delete,
325 enableprofile=enableprofile,
328 enableprofile=enableprofile,
326 disableprofile=disableprofile, force=force)
329 disableprofile=disableprofile, force=force)
327
330
328 if importrules:
331 if importrules:
329 sparse.importfromfiles(repo, opts, pats, force=force)
332 sparse.importfromfiles(repo, opts, pats, force=force)
330
333
331 if clearrules:
334 if clearrules:
332 sparse.clearrules(repo, force=force)
335 sparse.clearrules(repo, force=force)
333
336
334 if refresh:
337 if refresh:
335 try:
338 try:
336 wlock = repo.wlock()
339 wlock = repo.wlock()
337 fcounts = map(
340 fcounts = map(
338 len,
341 len,
339 sparse.refreshwdir(repo, repo.status(), sparse.matcher(repo),
342 sparse.refreshwdir(repo, repo.status(), sparse.matcher(repo),
340 force=force))
343 force=force))
341 sparse.printchanges(ui, opts, added=fcounts[0], dropped=fcounts[1],
344 sparse.printchanges(ui, opts, added=fcounts[0], dropped=fcounts[1],
342 conflicting=fcounts[2])
345 conflicting=fcounts[2])
343 finally:
346 finally:
344 wlock.release()
347 wlock.release()
@@ -1,69 +1,67
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 not have sparse enabled
55 XXX: we should have a flag in `hg debugsparse` to list the sparse profile
56 $ cat .hg/sparse
56 $ test -f .hg/sparse
57 [include]
57 [1]
58 inside/f
59
58
60 $ cat .hg/requires
59 $ cat .hg/requires
61 dotencode
60 dotencode
62 exp-sparse
63 fncache
61 fncache
64 generaldelta
62 generaldelta
65 narrowhg-experimental
63 narrowhg-experimental
66 revlogv1
64 revlogv1
67 sparserevlog
65 sparserevlog
68 store
66 store
69 treemanifest (tree !)
67 treemanifest (tree !)
General Comments 0
You need to be logged in to leave comments. Login now