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) |
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. |
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): |
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) |
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 ) |