##// END OF EJS Templates
sparse: fix a py2 based usage of `map()`...
Matt Harbison -
r50757:b1147450 default
parent child Browse files
Show More
@@ -1,393 +1,393
1 1 # sparse.py - allow sparse checkouts of the working directory
2 2 #
3 3 # Copyright 2014 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """allow sparse checkouts of the working directory (EXPERIMENTAL)
9 9
10 10 (This extension is not yet protected by backwards compatibility
11 11 guarantees. Any aspect may break in future releases until this
12 12 notice is removed.)
13 13
14 14 This extension allows the working directory to only consist of a
15 15 subset of files for the revision. This allows specific files or
16 16 directories to be explicitly included or excluded. Many repository
17 17 operations have performance proportional to the number of files in
18 18 the working directory. So only realizing a subset of files in the
19 19 working directory can improve performance.
20 20
21 21 Sparse Config Files
22 22 -------------------
23 23
24 24 The set of files that are part of a sparse checkout are defined by
25 25 a sparse config file. The file defines 3 things: includes (files to
26 26 include in the sparse checkout), excludes (files to exclude from the
27 27 sparse checkout), and profiles (links to other config files).
28 28
29 29 The file format is newline delimited. Empty lines and lines beginning
30 30 with ``#`` are ignored.
31 31
32 32 Lines beginning with ``%include `` denote another sparse config file
33 33 to include. e.g. ``%include tests.sparse``. The filename is relative
34 34 to the repository root.
35 35
36 36 The special lines ``[include]`` and ``[exclude]`` denote the section
37 37 for includes and excludes that follow, respectively. It is illegal to
38 38 have ``[include]`` after ``[exclude]``.
39 39
40 40 Non-special lines resemble file patterns to be added to either includes
41 41 or excludes. The syntax of these lines is documented by :hg:`help patterns`.
42 42 Patterns are interpreted as ``glob:`` by default and match against the
43 43 root of the repository.
44 44
45 45 Exclusion patterns take precedence over inclusion patterns. So even
46 46 if a file is explicitly included, an ``[exclude]`` entry can remove it.
47 47
48 48 For example, say you have a repository with 3 directories, ``frontend/``,
49 49 ``backend/``, and ``tools/``. ``frontend/`` and ``backend/`` correspond
50 50 to different projects and it is uncommon for someone working on one
51 51 to need the files for the other. But ``tools/`` contains files shared
52 52 between both projects. Your sparse config files may resemble::
53 53
54 54 # frontend.sparse
55 55 frontend/**
56 56 tools/**
57 57
58 58 # backend.sparse
59 59 backend/**
60 60 tools/**
61 61
62 62 Say the backend grows in size. Or there's a directory with thousands
63 63 of files you wish to exclude. You can modify the profile to exclude
64 64 certain files::
65 65
66 66 [include]
67 67 backend/**
68 68 tools/**
69 69
70 70 [exclude]
71 71 tools/tests/**
72 72 """
73 73
74 74
75 75 from mercurial.i18n import _
76 76 from mercurial.pycompat import setattr
77 77 from mercurial import (
78 78 cmdutil,
79 79 commands,
80 80 error,
81 81 extensions,
82 82 logcmdutil,
83 83 merge as mergemod,
84 84 pycompat,
85 85 registrar,
86 86 sparse,
87 87 util,
88 88 )
89 89
90 90 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
91 91 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
92 92 # be specifying the version(s) of Mercurial they are tested with, or
93 93 # leave the attribute unspecified.
94 94 testedwith = b'ships-with-hg-core'
95 95
96 96 cmdtable = {}
97 97 command = registrar.command(cmdtable)
98 98
99 99
100 100 def extsetup(ui):
101 101 sparse.enabled = True
102 102
103 103 _setupclone(ui)
104 104 _setuplog(ui)
105 105 _setupadd(ui)
106 106
107 107
108 108 def replacefilecache(cls, propname, replacement):
109 109 """Replace a filecache property with a new class. This allows changing the
110 110 cache invalidation condition."""
111 111 origcls = cls
112 112 assert callable(replacement)
113 113 while cls is not object:
114 114 if propname in cls.__dict__:
115 115 orig = cls.__dict__[propname]
116 116 setattr(cls, propname, replacement(orig))
117 117 break
118 118 cls = cls.__bases__[0]
119 119
120 120 if cls is object:
121 121 raise AttributeError(
122 122 _(b"type '%s' has no property '%s'") % (origcls, propname)
123 123 )
124 124
125 125
126 126 def _setuplog(ui):
127 127 entry = commands.table[b'log|history']
128 128 entry[1].append(
129 129 (
130 130 b'',
131 131 b'sparse',
132 132 None,
133 133 b"limit to changesets affecting the sparse checkout",
134 134 )
135 135 )
136 136
137 137 def _initialrevs(orig, repo, wopts):
138 138 revs = orig(repo, wopts)
139 139 if wopts.opts.get(b'sparse'):
140 140 sparsematch = sparse.matcher(repo)
141 141
142 142 def ctxmatch(rev):
143 143 ctx = repo[rev]
144 144 return any(f for f in ctx.files() if sparsematch(f))
145 145
146 146 revs = revs.filter(ctxmatch)
147 147 return revs
148 148
149 149 extensions.wrapfunction(logcmdutil, b'_initialrevs', _initialrevs)
150 150
151 151
152 152 def _clonesparsecmd(orig, ui, repo, *args, **opts):
153 153 include = opts.get('include')
154 154 exclude = opts.get('exclude')
155 155 enableprofile = opts.get('enable_profile')
156 156 narrow_pat = opts.get('narrow')
157 157
158 158 # if --narrow is passed, it means they are includes and excludes for narrow
159 159 # clone
160 160 if not narrow_pat and (include or exclude or enableprofile):
161 161
162 162 def clonesparse(orig, ctx, *args, **kwargs):
163 163 sparse.updateconfig(
164 164 ctx.repo().unfiltered(),
165 165 {},
166 166 include=include,
167 167 exclude=exclude,
168 168 enableprofile=enableprofile,
169 169 usereporootpaths=True,
170 170 )
171 171 return orig(ctx, *args, **kwargs)
172 172
173 173 extensions.wrapfunction(mergemod, b'update', clonesparse)
174 174 return orig(ui, repo, *args, **opts)
175 175
176 176
177 177 def _setupclone(ui):
178 178 entry = commands.table[b'clone']
179 179 entry[1].append((b'', b'enable-profile', [], b'enable a sparse profile'))
180 180 entry[1].append((b'', b'include', [], b'include sparse pattern'))
181 181 entry[1].append((b'', b'exclude', [], b'exclude sparse pattern'))
182 182 extensions.wrapcommand(commands.table, b'clone', _clonesparsecmd)
183 183
184 184
185 185 def _setupadd(ui):
186 186 entry = commands.table[b'add']
187 187 entry[1].append(
188 188 (
189 189 b's',
190 190 b'sparse',
191 191 None,
192 192 b'also include directories of added files in sparse config',
193 193 )
194 194 )
195 195
196 196 def _add(orig, ui, repo, *pats, **opts):
197 197 if opts.get('sparse'):
198 198 dirs = set()
199 199 for pat in pats:
200 200 dirname, basename = util.split(pat)
201 201 dirs.add(dirname)
202 202 sparse.updateconfig(repo, opts, include=list(dirs))
203 203 return orig(ui, repo, *pats, **opts)
204 204
205 205 extensions.wrapcommand(commands.table, b'add', _add)
206 206
207 207
208 208 @command(
209 209 b'debugsparse',
210 210 [
211 211 (
212 212 b'I',
213 213 b'include',
214 214 [],
215 215 _(b'include files in the sparse checkout'),
216 216 _(b'PATTERN'),
217 217 ),
218 218 (
219 219 b'X',
220 220 b'exclude',
221 221 [],
222 222 _(b'exclude files in the sparse checkout'),
223 223 _(b'PATTERN'),
224 224 ),
225 225 (
226 226 b'd',
227 227 b'delete',
228 228 [],
229 229 _(b'delete an include/exclude rule'),
230 230 _(b'PATTERN'),
231 231 ),
232 232 (
233 233 b'f',
234 234 b'force',
235 235 False,
236 236 _(b'allow changing rules even with pending changes'),
237 237 ),
238 238 (
239 239 b'',
240 240 b'enable-profile',
241 241 [],
242 242 _(b'enables the specified profile'),
243 243 _(b'PATTERN'),
244 244 ),
245 245 (
246 246 b'',
247 247 b'disable-profile',
248 248 [],
249 249 _(b'disables the specified profile'),
250 250 _(b'PATTERN'),
251 251 ),
252 252 (
253 253 b'',
254 254 b'import-rules',
255 255 [],
256 256 _(b'imports rules from a file'),
257 257 _(b'PATTERN'),
258 258 ),
259 259 (b'', b'clear-rules', False, _(b'clears local include/exclude rules')),
260 260 (
261 261 b'',
262 262 b'refresh',
263 263 False,
264 264 _(b'updates the working after sparseness changes'),
265 265 ),
266 266 (b'', b'reset', False, _(b'makes the repo full again')),
267 267 ]
268 268 + commands.templateopts,
269 269 _(b'[--OPTION]'),
270 270 helpbasic=True,
271 271 )
272 272 def debugsparse(ui, repo, **opts):
273 273 """make the current checkout sparse, or edit the existing checkout
274 274
275 275 The sparse command is used to make the current checkout sparse.
276 276 This means files that don't meet the sparse condition will not be
277 277 written to disk, or show up in any working copy operations. It does
278 278 not affect files in history in any way.
279 279
280 280 Passing no arguments prints the currently applied sparse rules.
281 281
282 282 --include and --exclude are used to add and remove files from the sparse
283 283 checkout. The effects of adding an include or exclude rule are applied
284 284 immediately. If applying the new rule would cause a file with pending
285 285 changes to be added or removed, the command will fail. Pass --force to
286 286 force a rule change even with pending changes (the changes on disk will
287 287 be preserved).
288 288
289 289 --delete removes an existing include/exclude rule. The effects are
290 290 immediate.
291 291
292 292 --refresh refreshes the files on disk based on the sparse rules. This is
293 293 only necessary if .hg/sparse was changed by hand.
294 294
295 295 --enable-profile and --disable-profile accept a path to a .hgsparse file.
296 296 This allows defining sparse checkouts and tracking them inside the
297 297 repository. This is useful for defining commonly used sparse checkouts for
298 298 many people to use. As the profile definition changes over time, the sparse
299 299 checkout will automatically be updated appropriately, depending on which
300 300 changeset is checked out. Changes to .hgsparse are not applied until they
301 301 have been committed.
302 302
303 303 --import-rules accepts a path to a file containing rules in the .hgsparse
304 304 format, allowing you to add --include, --exclude and --enable-profile rules
305 305 in bulk. Like the --include, --exclude and --enable-profile switches, the
306 306 changes are applied immediately.
307 307
308 308 --clear-rules removes all local include and exclude rules, while leaving
309 309 any enabled profiles in place.
310 310
311 311 Returns 0 if editing the sparse checkout succeeds.
312 312 """
313 313 opts = pycompat.byteskwargs(opts)
314 314 include = opts.get(b'include')
315 315 exclude = opts.get(b'exclude')
316 316 force = opts.get(b'force')
317 317 enableprofile = opts.get(b'enable_profile')
318 318 disableprofile = opts.get(b'disable_profile')
319 319 importrules = opts.get(b'import_rules')
320 320 clearrules = opts.get(b'clear_rules')
321 321 delete = opts.get(b'delete')
322 322 refresh = opts.get(b'refresh')
323 323 reset = opts.get(b'reset')
324 324 action = cmdutil.check_at_most_one_arg(
325 325 opts, b'import_rules', b'clear_rules', b'refresh'
326 326 )
327 327 updateconfig = bool(
328 328 include or exclude or delete or reset or enableprofile or disableprofile
329 329 )
330 330 count = sum([updateconfig, bool(action)])
331 331 if count > 1:
332 332 raise error.Abort(_(b"too many flags specified"))
333 333
334 334 # enable sparse on repo even if the requirements is missing.
335 335 repo._has_sparse = True
336 336
337 337 if count == 0:
338 338 if repo.vfs.exists(b'sparse'):
339 339 ui.status(repo.vfs.read(b"sparse") + b"\n")
340 340 temporaryincludes = sparse.readtemporaryincludes(repo)
341 341 if temporaryincludes:
342 342 ui.status(
343 343 _(b"Temporarily Included Files (for merge/rebase):\n")
344 344 )
345 345 ui.status((b"\n".join(temporaryincludes) + b"\n"))
346 346 return
347 347 else:
348 348 raise error.Abort(
349 349 _(
350 350 b'the debugsparse command is only supported on'
351 351 b' sparse repositories'
352 352 )
353 353 )
354 354
355 355 if updateconfig:
356 356 sparse.updateconfig(
357 357 repo,
358 358 opts,
359 359 include=include,
360 360 exclude=exclude,
361 361 reset=reset,
362 362 delete=delete,
363 363 enableprofile=enableprofile,
364 364 disableprofile=disableprofile,
365 365 force=force,
366 366 )
367 367
368 368 if importrules:
369 369 sparse.importfromfiles(repo, opts, importrules, force=force)
370 370
371 371 if clearrules:
372 372 sparse.clearrules(repo, force=force)
373 373
374 374 if refresh:
375 375 try:
376 376 wlock = repo.wlock()
377 fcounts = map(
377 fcounts = pycompat.maplist(
378 378 len,
379 379 sparse.refreshwdir(
380 380 repo, repo.status(), sparse.matcher(repo), force=force
381 381 ),
382 382 )
383 383 sparse.printchanges(
384 384 ui,
385 385 opts,
386 386 added=fcounts[0],
387 387 dropped=fcounts[1],
388 388 conflicting=fcounts[2],
389 389 )
390 390 finally:
391 391 wlock.release()
392 392
393 393 del repo._has_sparse
General Comments 0
You need to be logged in to leave comments. Login now