comparison mercurial/sparse.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children d783f945a701
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
42 excludes = set() 42 excludes = set()
43 profiles = set() 43 profiles = set()
44 current = None 44 current = None
45 havesection = False 45 havesection = False
46 46
47 for line in raw.split('\n'): 47 for line in raw.split(b'\n'):
48 line = line.strip() 48 line = line.strip()
49 if not line or line.startswith('#'): 49 if not line or line.startswith(b'#'):
50 # empty or comment line, skip 50 # empty or comment line, skip
51 continue 51 continue
52 elif line.startswith('%include '): 52 elif line.startswith(b'%include '):
53 line = line[9:].strip() 53 line = line[9:].strip()
54 if line: 54 if line:
55 profiles.add(line) 55 profiles.add(line)
56 elif line == '[include]': 56 elif line == b'[include]':
57 if havesection and current != includes: 57 if havesection and current != includes:
58 # TODO pass filename into this API so we can report it. 58 # TODO pass filename into this API so we can report it.
59 raise error.Abort( 59 raise error.Abort(
60 _( 60 _(
61 '%(action)s config cannot have includes ' 61 b'%(action)s config cannot have includes '
62 'after excludes' 62 b'after excludes'
63 ) 63 )
64 % {'action': action} 64 % {b'action': action}
65 ) 65 )
66 havesection = True 66 havesection = True
67 current = includes 67 current = includes
68 continue 68 continue
69 elif line == '[exclude]': 69 elif line == b'[exclude]':
70 havesection = True 70 havesection = True
71 current = excludes 71 current = excludes
72 elif line: 72 elif line:
73 if current is None: 73 if current is None:
74 raise error.Abort( 74 raise error.Abort(
75 _('%(action)s config entry outside of ' 'section: %(line)s') 75 _(
76 % {'action': action, 'line': line}, 76 b'%(action)s config entry outside of '
77 b'section: %(line)s'
78 )
79 % {b'action': action, b'line': line},
77 hint=_( 80 hint=_(
78 'add an [include] or [exclude] line ' 81 b'add an [include] or [exclude] line '
79 'to declare the entry type' 82 b'to declare the entry type'
80 ), 83 ),
81 ) 84 )
82 85
83 if line.strip().startswith('/'): 86 if line.strip().startswith(b'/'):
84 ui.warn( 87 ui.warn(
85 _( 88 _(
86 'warning: %(action)s profile cannot use' 89 b'warning: %(action)s profile cannot use'
87 ' paths starting with /, ignoring %(line)s\n' 90 b' paths starting with /, ignoring %(line)s\n'
88 ) 91 )
89 % {'action': action, 'line': line} 92 % {b'action': action, b'line': line}
90 ) 93 )
91 continue 94 continue
92 current.add(line) 95 current.add(line)
93 96
94 return includes, excludes, profiles 97 return includes, excludes, profiles
110 """ 113 """
111 # Feature isn't enabled. No-op. 114 # Feature isn't enabled. No-op.
112 if not enabled: 115 if not enabled:
113 return set(), set(), set() 116 return set(), set(), set()
114 117
115 raw = repo.vfs.tryread('sparse') 118 raw = repo.vfs.tryread(b'sparse')
116 if not raw: 119 if not raw:
117 return set(), set(), set() 120 return set(), set(), set()
118 121
119 if rev is None: 122 if rev is None:
120 raise error.Abort( 123 raise error.Abort(
121 _('cannot parse sparse patterns from working ' 'directory') 124 _(b'cannot parse sparse patterns from working ' b'directory')
122 ) 125 )
123 126
124 includes, excludes, profiles = parseconfig(repo.ui, raw, 'sparse') 127 includes, excludes, profiles = parseconfig(repo.ui, raw, b'sparse')
125 ctx = repo[rev] 128 ctx = repo[rev]
126 129
127 if profiles: 130 if profiles:
128 visited = set() 131 visited = set()
129 while profiles: 132 while profiles:
135 138
136 try: 139 try:
137 raw = readprofile(repo, profile, rev) 140 raw = readprofile(repo, profile, rev)
138 except error.ManifestLookupError: 141 except error.ManifestLookupError:
139 msg = ( 142 msg = (
140 "warning: sparse profile '%s' not found " 143 b"warning: sparse profile '%s' not found "
141 "in rev %s - ignoring it\n" % (profile, ctx) 144 b"in rev %s - ignoring it\n" % (profile, ctx)
142 ) 145 )
143 # experimental config: sparse.missingwarning 146 # experimental config: sparse.missingwarning
144 if repo.ui.configbool('sparse', 'missingwarning'): 147 if repo.ui.configbool(b'sparse', b'missingwarning'):
145 repo.ui.warn(msg) 148 repo.ui.warn(msg)
146 else: 149 else:
147 repo.ui.debug(msg) 150 repo.ui.debug(msg)
148 continue 151 continue
149 152
150 pincludes, pexcludes, subprofs = parseconfig(repo.ui, raw, 'sparse') 153 pincludes, pexcludes, subprofs = parseconfig(
154 repo.ui, raw, b'sparse'
155 )
151 includes.update(pincludes) 156 includes.update(pincludes)
152 excludes.update(pexcludes) 157 excludes.update(pexcludes)
153 profiles.update(subprofs) 158 profiles.update(subprofs)
154 159
155 profiles = visited 160 profiles = visited
156 161
157 if includes: 162 if includes:
158 includes.add('.hg*') 163 includes.add(b'.hg*')
159 164
160 return includes, excludes, profiles 165 return includes, excludes, profiles
161 166
162 167
163 def activeconfig(repo): 168 def activeconfig(repo):
190 195
191 This is used to construct a cache key for matchers. 196 This is used to construct a cache key for matchers.
192 """ 197 """
193 cache = repo._sparsesignaturecache 198 cache = repo._sparsesignaturecache
194 199
195 signature = cache.get('signature') 200 signature = cache.get(b'signature')
196 201
197 if includetemp: 202 if includetemp:
198 tempsignature = cache.get('tempsignature') 203 tempsignature = cache.get(b'tempsignature')
199 else: 204 else:
200 tempsignature = '0' 205 tempsignature = b'0'
201 206
202 if signature is None or (includetemp and tempsignature is None): 207 if signature is None or (includetemp and tempsignature is None):
203 signature = hex(hashlib.sha1(repo.vfs.tryread('sparse')).digest()) 208 signature = hex(hashlib.sha1(repo.vfs.tryread(b'sparse')).digest())
204 cache['signature'] = signature 209 cache[b'signature'] = signature
205 210
206 if includetemp: 211 if includetemp:
207 raw = repo.vfs.tryread('tempsparse') 212 raw = repo.vfs.tryread(b'tempsparse')
208 tempsignature = hex(hashlib.sha1(raw).digest()) 213 tempsignature = hex(hashlib.sha1(raw).digest())
209 cache['tempsignature'] = tempsignature 214 cache[b'tempsignature'] = tempsignature
210 215
211 return '%s %s' % (signature, tempsignature) 216 return b'%s %s' % (signature, tempsignature)
212 217
213 218
214 def writeconfig(repo, includes, excludes, profiles): 219 def writeconfig(repo, includes, excludes, profiles):
215 """Write the sparse config file given a sparse configuration.""" 220 """Write the sparse config file given a sparse configuration."""
216 with repo.vfs('sparse', 'wb') as fh: 221 with repo.vfs(b'sparse', b'wb') as fh:
217 for p in sorted(profiles): 222 for p in sorted(profiles):
218 fh.write('%%include %s\n' % p) 223 fh.write(b'%%include %s\n' % p)
219 224
220 if includes: 225 if includes:
221 fh.write('[include]\n') 226 fh.write(b'[include]\n')
222 for i in sorted(includes): 227 for i in sorted(includes):
223 fh.write(i) 228 fh.write(i)
224 fh.write('\n') 229 fh.write(b'\n')
225 230
226 if excludes: 231 if excludes:
227 fh.write('[exclude]\n') 232 fh.write(b'[exclude]\n')
228 for e in sorted(excludes): 233 for e in sorted(excludes):
229 fh.write(e) 234 fh.write(e)
230 fh.write('\n') 235 fh.write(b'\n')
231 236
232 repo._sparsesignaturecache.clear() 237 repo._sparsesignaturecache.clear()
233 238
234 239
235 def readtemporaryincludes(repo): 240 def readtemporaryincludes(repo):
236 raw = repo.vfs.tryread('tempsparse') 241 raw = repo.vfs.tryread(b'tempsparse')
237 if not raw: 242 if not raw:
238 return set() 243 return set()
239 244
240 return set(raw.split('\n')) 245 return set(raw.split(b'\n'))
241 246
242 247
243 def writetemporaryincludes(repo, includes): 248 def writetemporaryincludes(repo, includes):
244 repo.vfs.write('tempsparse', '\n'.join(sorted(includes))) 249 repo.vfs.write(b'tempsparse', b'\n'.join(sorted(includes)))
245 repo._sparsesignaturecache.clear() 250 repo._sparsesignaturecache.clear()
246 251
247 252
248 def addtemporaryincludes(repo, additional): 253 def addtemporaryincludes(repo, additional):
249 includes = readtemporaryincludes(repo) 254 includes = readtemporaryincludes(repo)
251 includes.add(i) 256 includes.add(i)
252 writetemporaryincludes(repo, includes) 257 writetemporaryincludes(repo, includes)
253 258
254 259
255 def prunetemporaryincludes(repo): 260 def prunetemporaryincludes(repo):
256 if not enabled or not repo.vfs.exists('tempsparse'): 261 if not enabled or not repo.vfs.exists(b'tempsparse'):
257 return 262 return
258 263
259 s = repo.status() 264 s = repo.status()
260 if s.modified or s.added or s.removed or s.deleted: 265 if s.modified or s.added or s.removed or s.deleted:
261 # Still have pending changes. Don't bother trying to prune. 266 # Still have pending changes. Don't bother trying to prune.
266 actions = [] 271 actions = []
267 dropped = [] 272 dropped = []
268 tempincludes = readtemporaryincludes(repo) 273 tempincludes = readtemporaryincludes(repo)
269 for file in tempincludes: 274 for file in tempincludes:
270 if file in dirstate and not sparsematch(file): 275 if file in dirstate and not sparsematch(file):
271 message = _('dropping temporarily included sparse files') 276 message = _(b'dropping temporarily included sparse files')
272 actions.append((file, None, message)) 277 actions.append((file, None, message))
273 dropped.append(file) 278 dropped.append(file)
274 279
275 typeactions = mergemod.emptyactions() 280 typeactions = mergemod.emptyactions()
276 typeactions['r'] = actions 281 typeactions[b'r'] = actions
277 mergemod.applyupdates( 282 mergemod.applyupdates(
278 repo, typeactions, repo[None], repo['.'], False, wantfiledata=False 283 repo, typeactions, repo[None], repo[b'.'], False, wantfiledata=False
279 ) 284 )
280 285
281 # Fix dirstate 286 # Fix dirstate
282 for file in dropped: 287 for file in dropped:
283 dirstate.drop(file) 288 dirstate.drop(file)
284 289
285 repo.vfs.unlink('tempsparse') 290 repo.vfs.unlink(b'tempsparse')
286 repo._sparsesignaturecache.clear() 291 repo._sparsesignaturecache.clear()
287 msg = _( 292 msg = _(
288 'cleaned up %d temporarily added file(s) from the ' 'sparse checkout\n' 293 b'cleaned up %d temporarily added file(s) from the '
294 b'sparse checkout\n'
289 ) 295 )
290 repo.ui.status(msg % len(tempincludes)) 296 repo.ui.status(msg % len(tempincludes))
291 297
292 298
293 def forceincludematcher(matcher, includes): 299 def forceincludematcher(matcher, includes):
294 """Returns a matcher that returns true for any of the forced includes 300 """Returns a matcher that returns true for any of the forced includes
295 before testing against the actual matcher.""" 301 before testing against the actual matcher."""
296 kindpats = [('path', include, '') for include in includes] 302 kindpats = [(b'path', include, b'') for include in includes]
297 includematcher = matchmod.includematcher('', kindpats) 303 includematcher = matchmod.includematcher(b'', kindpats)
298 return matchmod.unionmatcher([includematcher, matcher]) 304 return matchmod.unionmatcher([includematcher, matcher])
299 305
300 306
301 def matcher(repo, revs=None, includetemp=True): 307 def matcher(repo, revs=None, includetemp=True):
302 """Obtain a matcher for sparse working directories for the given revs. 308 """Obtain a matcher for sparse working directories for the given revs.
317 if node != nullid 323 if node != nullid
318 ] 324 ]
319 325
320 signature = configsignature(repo, includetemp=includetemp) 326 signature = configsignature(repo, includetemp=includetemp)
321 327
322 key = '%s %s' % (signature, ' '.join(map(pycompat.bytestr, revs))) 328 key = b'%s %s' % (signature, b' '.join(map(pycompat.bytestr, revs)))
323 329
324 result = repo._sparsematchercache.get(key) 330 result = repo._sparsematchercache.get(key)
325 if result: 331 if result:
326 return result 332 return result
327 333
331 includes, excludes, profiles = patternsforrev(repo, rev) 337 includes, excludes, profiles = patternsforrev(repo, rev)
332 338
333 if includes or excludes: 339 if includes or excludes:
334 matcher = matchmod.match( 340 matcher = matchmod.match(
335 repo.root, 341 repo.root,
336 '', 342 b'',
337 [], 343 [],
338 include=includes, 344 include=includes,
339 exclude=excludes, 345 exclude=excludes,
340 default='relpath', 346 default=b'relpath',
341 ) 347 )
342 matchers.append(matcher) 348 matchers.append(matcher)
343 except IOError: 349 except IOError:
344 pass 350 pass
345 351
386 for file, action in actions.iteritems(): 392 for file, action in actions.iteritems():
387 type, args, msg = action 393 type, args, msg = action
388 files.add(file) 394 files.add(file)
389 if sparsematch(file): 395 if sparsematch(file):
390 prunedactions[file] = action 396 prunedactions[file] = action
391 elif type == 'm': 397 elif type == b'm':
392 temporaryfiles.append(file) 398 temporaryfiles.append(file)
393 prunedactions[file] = action 399 prunedactions[file] = action
394 elif branchmerge: 400 elif branchmerge:
395 if type != 'k': 401 if type != b'k':
396 temporaryfiles.append(file) 402 temporaryfiles.append(file)
397 prunedactions[file] = action 403 prunedactions[file] = action
398 elif type == 'f': 404 elif type == b'f':
399 prunedactions[file] = action 405 prunedactions[file] = action
400 elif file in wctx: 406 elif file in wctx:
401 prunedactions[file] = ('r', args, msg) 407 prunedactions[file] = (b'r', args, msg)
402 408
403 if branchmerge and type == mergemod.ACTION_MERGE: 409 if branchmerge and type == mergemod.ACTION_MERGE:
404 f1, f2, fa, move, anc = args 410 f1, f2, fa, move, anc = args
405 if not sparsematch(f1): 411 if not sparsematch(f1):
406 temporaryfiles.append(f1) 412 temporaryfiles.append(f1)
407 413
408 if len(temporaryfiles) > 0: 414 if len(temporaryfiles) > 0:
409 repo.ui.status( 415 repo.ui.status(
410 _( 416 _(
411 'temporarily included %d file(s) in the sparse ' 417 b'temporarily included %d file(s) in the sparse '
412 'checkout for merging\n' 418 b'checkout for merging\n'
413 ) 419 )
414 % len(temporaryfiles) 420 % len(temporaryfiles)
415 ) 421 )
416 addtemporaryincludes(repo, temporaryfiles) 422 addtemporaryincludes(repo, temporaryfiles)
417 423
418 # Add the new files to the working copy so they can be merged, etc 424 # Add the new files to the working copy so they can be merged, etc
419 actions = [] 425 actions = []
420 message = 'temporarily adding to sparse checkout' 426 message = b'temporarily adding to sparse checkout'
421 wctxmanifest = repo[None].manifest() 427 wctxmanifest = repo[None].manifest()
422 for file in temporaryfiles: 428 for file in temporaryfiles:
423 if file in wctxmanifest: 429 if file in wctxmanifest:
424 fctx = repo[None][file] 430 fctx = repo[None][file]
425 actions.append((file, (fctx.flags(), False), message)) 431 actions.append((file, (fctx.flags(), False), message))
426 432
427 typeactions = mergemod.emptyactions() 433 typeactions = mergemod.emptyactions()
428 typeactions['g'] = actions 434 typeactions[b'g'] = actions
429 mergemod.applyupdates( 435 mergemod.applyupdates(
430 repo, typeactions, repo[None], repo['.'], False, wantfiledata=False 436 repo, typeactions, repo[None], repo[b'.'], False, wantfiledata=False
431 ) 437 )
432 438
433 dirstate = repo.dirstate 439 dirstate = repo.dirstate
434 for file, flags, msg in actions: 440 for file, flags, msg in actions:
435 dirstate.normal(file) 441 dirstate.normal(file)
444 for file in mf: 450 for file in mf:
445 old = oldsparsematch(file) 451 old = oldsparsematch(file)
446 new = sparsematch(file) 452 new = sparsematch(file)
447 if not old and new: 453 if not old and new:
448 flags = mf.flags(file) 454 flags = mf.flags(file)
449 prunedactions[file] = ('g', (flags, False), '') 455 prunedactions[file] = (b'g', (flags, False), b'')
450 elif old and not new: 456 elif old and not new:
451 prunedactions[file] = ('r', [], '') 457 prunedactions[file] = (b'r', [], b'')
452 458
453 return prunedactions 459 return prunedactions
454 460
455 461
456 def refreshwdir(repo, origstatus, origsparsematch, force=False): 462 def refreshwdir(repo, origstatus, origsparsematch, force=False):
470 sparsematch = matcher(repo) 476 sparsematch = matcher(repo)
471 abort = False 477 abort = False
472 478
473 for f in pending: 479 for f in pending:
474 if not sparsematch(f): 480 if not sparsematch(f):
475 repo.ui.warn(_("pending changes to '%s'\n") % f) 481 repo.ui.warn(_(b"pending changes to '%s'\n") % f)
476 abort = not force 482 abort = not force
477 483
478 if abort: 484 if abort:
479 raise error.Abort( 485 raise error.Abort(
480 _('could not update sparseness due to pending ' 'changes') 486 _(b'could not update sparseness due to pending ' b'changes')
481 ) 487 )
482 488
483 # Calculate actions 489 # Calculate actions
484 dirstate = repo.dirstate 490 dirstate = repo.dirstate
485 ctx = repo['.'] 491 ctx = repo[b'.']
486 added = [] 492 added = []
487 lookup = [] 493 lookup = []
488 dropped = [] 494 dropped = []
489 mf = ctx.manifest() 495 mf = ctx.manifest()
490 files = set(mf) 496 files = set(mf)
497 # Add files that are newly included, or that don't exist in 503 # Add files that are newly included, or that don't exist in
498 # the dirstate yet. 504 # the dirstate yet.
499 if (new and not old) or (old and new and not file in dirstate): 505 if (new and not old) or (old and new and not file in dirstate):
500 fl = mf.flags(file) 506 fl = mf.flags(file)
501 if repo.wvfs.exists(file): 507 if repo.wvfs.exists(file):
502 actions[file] = ('e', (fl,), '') 508 actions[file] = (b'e', (fl,), b'')
503 lookup.append(file) 509 lookup.append(file)
504 else: 510 else:
505 actions[file] = ('g', (fl, False), '') 511 actions[file] = (b'g', (fl, False), b'')
506 added.append(file) 512 added.append(file)
507 # Drop files that are newly excluded, or that still exist in 513 # Drop files that are newly excluded, or that still exist in
508 # the dirstate. 514 # the dirstate.
509 elif (old and not new) or (not old and not new and file in dirstate): 515 elif (old and not new) or (not old and not new and file in dirstate):
510 dropped.append(file) 516 dropped.append(file)
511 if file not in pending: 517 if file not in pending:
512 actions[file] = ('r', [], '') 518 actions[file] = (b'r', [], b'')
513 519
514 # Verify there are no pending changes in newly included files 520 # Verify there are no pending changes in newly included files
515 abort = False 521 abort = False
516 for file in lookup: 522 for file in lookup:
517 repo.ui.warn(_("pending changes to '%s'\n") % file) 523 repo.ui.warn(_(b"pending changes to '%s'\n") % file)
518 abort = not force 524 abort = not force
519 if abort: 525 if abort:
520 raise error.Abort( 526 raise error.Abort(
521 _( 527 _(
522 'cannot change sparseness due to pending ' 528 b'cannot change sparseness due to pending '
523 'changes (delete the files or use ' 529 b'changes (delete the files or use '
524 '--force to bring them back dirty)' 530 b'--force to bring them back dirty)'
525 ) 531 )
526 ) 532 )
527 533
528 # Check for files that were only in the dirstate. 534 # Check for files that were only in the dirstate.
529 for file, state in dirstate.iteritems(): 535 for file, state in dirstate.iteritems():
537 typeactions = mergemod.emptyactions() 543 typeactions = mergemod.emptyactions()
538 for f, (m, args, msg) in actions.iteritems(): 544 for f, (m, args, msg) in actions.iteritems():
539 typeactions[m].append((f, args, msg)) 545 typeactions[m].append((f, args, msg))
540 546
541 mergemod.applyupdates( 547 mergemod.applyupdates(
542 repo, typeactions, repo[None], repo['.'], False, wantfiledata=False 548 repo, typeactions, repo[None], repo[b'.'], False, wantfiledata=False
543 ) 549 )
544 550
545 # Fix dirstate 551 # Fix dirstate
546 for file in added: 552 for file in added:
547 dirstate.normal(file) 553 dirstate.normal(file)
575 581
576 def _updateconfigandrefreshwdir( 582 def _updateconfigandrefreshwdir(
577 repo, includes, excludes, profiles, force=False, removing=False 583 repo, includes, excludes, profiles, force=False, removing=False
578 ): 584 ):
579 """Update the sparse config and working directory state.""" 585 """Update the sparse config and working directory state."""
580 raw = repo.vfs.tryread('sparse') 586 raw = repo.vfs.tryread(b'sparse')
581 oldincludes, oldexcludes, oldprofiles = parseconfig(repo.ui, raw, 'sparse') 587 oldincludes, oldexcludes, oldprofiles = parseconfig(repo.ui, raw, b'sparse')
582 588
583 oldstatus = repo.status() 589 oldstatus = repo.status()
584 oldmatch = matcher(repo) 590 oldmatch = matcher(repo)
585 oldrequires = set(repo.requirements) 591 oldrequires = set(repo.requirements)
586 592
590 # re-read. We ideally want to update the cached matcher on the 596 # re-read. We ideally want to update the cached matcher on the
591 # repo instance then flush the new config to disk once wdir is 597 # repo instance then flush the new config to disk once wdir is
592 # updated. But this requires massive rework to matcher() and its 598 # updated. But this requires massive rework to matcher() and its
593 # consumers. 599 # consumers.
594 600
595 if 'exp-sparse' in oldrequires and removing: 601 if b'exp-sparse' in oldrequires and removing:
596 repo.requirements.discard('exp-sparse') 602 repo.requirements.discard(b'exp-sparse')
597 scmutil.writerequires(repo.vfs, repo.requirements) 603 scmutil.writerequires(repo.vfs, repo.requirements)
598 elif 'exp-sparse' not in oldrequires: 604 elif b'exp-sparse' not in oldrequires:
599 repo.requirements.add('exp-sparse') 605 repo.requirements.add(b'exp-sparse')
600 scmutil.writerequires(repo.vfs, repo.requirements) 606 scmutil.writerequires(repo.vfs, repo.requirements)
601 607
602 try: 608 try:
603 writeconfig(repo, includes, excludes, profiles) 609 writeconfig(repo, includes, excludes, profiles)
604 return refreshwdir(repo, oldstatus, oldmatch, force=force) 610 return refreshwdir(repo, oldstatus, oldmatch, force=force)
616 622
617 The remaining sparse config only has profiles, if defined. The working 623 The remaining sparse config only has profiles, if defined. The working
618 directory is refreshed, as needed. 624 directory is refreshed, as needed.
619 """ 625 """
620 with repo.wlock(): 626 with repo.wlock():
621 raw = repo.vfs.tryread('sparse') 627 raw = repo.vfs.tryread(b'sparse')
622 includes, excludes, profiles = parseconfig(repo.ui, raw, 'sparse') 628 includes, excludes, profiles = parseconfig(repo.ui, raw, b'sparse')
623 629
624 if not includes and not excludes: 630 if not includes and not excludes:
625 return 631 return
626 632
627 _updateconfigandrefreshwdir(repo, set(), set(), profiles, force=force) 633 _updateconfigandrefreshwdir(repo, set(), set(), profiles, force=force)
633 The updated sparse config is written out and the working directory 639 The updated sparse config is written out and the working directory
634 is refreshed, as needed. 640 is refreshed, as needed.
635 """ 641 """
636 with repo.wlock(): 642 with repo.wlock():
637 # read current configuration 643 # read current configuration
638 raw = repo.vfs.tryread('sparse') 644 raw = repo.vfs.tryread(b'sparse')
639 includes, excludes, profiles = parseconfig(repo.ui, raw, 'sparse') 645 includes, excludes, profiles = parseconfig(repo.ui, raw, b'sparse')
640 aincludes, aexcludes, aprofiles = activeconfig(repo) 646 aincludes, aexcludes, aprofiles = activeconfig(repo)
641 647
642 # Import rules on top; only take in rules that are not yet 648 # Import rules on top; only take in rules that are not yet
643 # part of the active rules. 649 # part of the active rules.
644 changed = False 650 changed = False
645 for p in paths: 651 for p in paths:
646 with util.posixfile(util.expandpath(p), mode='rb') as fh: 652 with util.posixfile(util.expandpath(p), mode=b'rb') as fh:
647 raw = fh.read() 653 raw = fh.read()
648 654
649 iincludes, iexcludes, iprofiles = parseconfig( 655 iincludes, iexcludes, iprofiles = parseconfig(
650 repo.ui, raw, 'sparse' 656 repo.ui, raw, b'sparse'
651 ) 657 )
652 oldsize = len(includes) + len(excludes) + len(profiles) 658 oldsize = len(includes) + len(excludes) + len(profiles)
653 includes.update(iincludes - aincludes) 659 includes.update(iincludes - aincludes)
654 excludes.update(iexcludes - aexcludes) 660 excludes.update(iexcludes - aexcludes)
655 profiles.update(iprofiles - aprofiles) 661 profiles.update(iprofiles - aprofiles)
694 Only one of the actions may be performed. 700 Only one of the actions may be performed.
695 701
696 The new config is written out and a working directory refresh is performed. 702 The new config is written out and a working directory refresh is performed.
697 """ 703 """
698 with repo.wlock(): 704 with repo.wlock():
699 raw = repo.vfs.tryread('sparse') 705 raw = repo.vfs.tryread(b'sparse')
700 oldinclude, oldexclude, oldprofiles = parseconfig( 706 oldinclude, oldexclude, oldprofiles = parseconfig(
701 repo.ui, raw, 'sparse' 707 repo.ui, raw, b'sparse'
702 ) 708 )
703 709
704 if reset: 710 if reset:
705 newinclude = set() 711 newinclude = set()
706 newexclude = set() 712 newexclude = set()
709 newinclude = set(oldinclude) 715 newinclude = set(oldinclude)
710 newexclude = set(oldexclude) 716 newexclude = set(oldexclude)
711 newprofiles = set(oldprofiles) 717 newprofiles = set(oldprofiles)
712 718
713 if any(os.path.isabs(pat) for pat in pats): 719 if any(os.path.isabs(pat) for pat in pats):
714 raise error.Abort(_('paths cannot be absolute')) 720 raise error.Abort(_(b'paths cannot be absolute'))
715 721
716 if not usereporootpaths: 722 if not usereporootpaths:
717 # let's treat paths as relative to cwd 723 # let's treat paths as relative to cwd
718 root, cwd = repo.root, repo.getcwd() 724 root, cwd = repo.root, repo.getcwd()
719 abspats = [] 725 abspats = []
720 for kindpat in pats: 726 for kindpat in pats:
721 kind, pat = matchmod._patsplit(kindpat, None) 727 kind, pat = matchmod._patsplit(kindpat, None)
722 if kind in matchmod.cwdrelativepatternkinds or kind is None: 728 if kind in matchmod.cwdrelativepatternkinds or kind is None:
723 ap = (kind + ':' if kind else '') + pathutil.canonpath( 729 ap = (kind + b':' if kind else b'') + pathutil.canonpath(
724 root, cwd, pat 730 root, cwd, pat
725 ) 731 )
726 abspats.append(ap) 732 abspats.append(ap)
727 else: 733 else:
728 abspats.append(kindpat) 734 abspats.append(kindpat)
776 added=0, 782 added=0,
777 dropped=0, 783 dropped=0,
778 conflicting=0, 784 conflicting=0,
779 ): 785 ):
780 """Print output summarizing sparse config changes.""" 786 """Print output summarizing sparse config changes."""
781 with ui.formatter('sparse', opts) as fm: 787 with ui.formatter(b'sparse', opts) as fm:
782 fm.startitem() 788 fm.startitem()
783 fm.condwrite( 789 fm.condwrite(
784 ui.verbose, 790 ui.verbose,
785 'profiles_added', 791 b'profiles_added',
786 _('Profiles changed: %d\n'), 792 _(b'Profiles changed: %d\n'),
787 profilecount, 793 profilecount,
788 ) 794 )
789 fm.condwrite( 795 fm.condwrite(
790 ui.verbose, 796 ui.verbose,
791 'include_rules_added', 797 b'include_rules_added',
792 _('Include rules changed: %d\n'), 798 _(b'Include rules changed: %d\n'),
793 includecount, 799 includecount,
794 ) 800 )
795 fm.condwrite( 801 fm.condwrite(
796 ui.verbose, 802 ui.verbose,
797 'exclude_rules_added', 803 b'exclude_rules_added',
798 _('Exclude rules changed: %d\n'), 804 _(b'Exclude rules changed: %d\n'),
799 excludecount, 805 excludecount,
800 ) 806 )
801 807
802 # In 'plain' verbose mode, mergemod.applyupdates already outputs what 808 # In 'plain' verbose mode, mergemod.applyupdates already outputs what
803 # files are added or removed outside of the templating formatter 809 # files are added or removed outside of the templating formatter
804 # framework. No point in repeating ourselves in that case. 810 # framework. No point in repeating ourselves in that case.
805 if not fm.isplain(): 811 if not fm.isplain():
806 fm.condwrite( 812 fm.condwrite(
807 ui.verbose, 'files_added', _('Files added: %d\n'), added 813 ui.verbose, b'files_added', _(b'Files added: %d\n'), added
808 ) 814 )
809 fm.condwrite( 815 fm.condwrite(
810 ui.verbose, 'files_dropped', _('Files dropped: %d\n'), dropped 816 ui.verbose, b'files_dropped', _(b'Files dropped: %d\n'), dropped
811 ) 817 )
812 fm.condwrite( 818 fm.condwrite(
813 ui.verbose, 819 ui.verbose,
814 'files_conflicting', 820 b'files_conflicting',
815 _('Files conflicting: %d\n'), 821 _(b'Files conflicting: %d\n'),
816 conflicting, 822 conflicting,
817 ) 823 )