comparison mercurial/cmdutil.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 eef9a2d67051
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
59 stringio = util.stringio 59 stringio = util.stringio
60 60
61 # templates of common command options 61 # templates of common command options
62 62
63 dryrunopts = [ 63 dryrunopts = [
64 ('n', 'dry-run', None, _('do not perform actions, just print output')), 64 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
65 ] 65 ]
66 66
67 confirmopts = [ 67 confirmopts = [
68 ('', 'confirm', None, _('ask before applying actions')), 68 (b'', b'confirm', None, _(b'ask before applying actions')),
69 ] 69 ]
70 70
71 remoteopts = [ 71 remoteopts = [
72 ('e', 'ssh', '', _('specify ssh command to use'), _('CMD')), 72 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
73 ( 73 (
74 '', 74 b'',
75 'remotecmd', 75 b'remotecmd',
76 '', 76 b'',
77 _('specify hg command to run on the remote side'), 77 _(b'specify hg command to run on the remote side'),
78 _('CMD'), 78 _(b'CMD'),
79 ), 79 ),
80 ( 80 (
81 '', 81 b'',
82 'insecure', 82 b'insecure',
83 None, 83 None,
84 _('do not verify server certificate (ignoring web.cacerts config)'), 84 _(b'do not verify server certificate (ignoring web.cacerts config)'),
85 ), 85 ),
86 ] 86 ]
87 87
88 walkopts = [ 88 walkopts = [
89 ( 89 (
90 'I', 90 b'I',
91 'include', 91 b'include',
92 [], 92 [],
93 _('include names matching the given patterns'), 93 _(b'include names matching the given patterns'),
94 _('PATTERN'), 94 _(b'PATTERN'),
95 ), 95 ),
96 ( 96 (
97 'X', 97 b'X',
98 'exclude', 98 b'exclude',
99 [], 99 [],
100 _('exclude names matching the given patterns'), 100 _(b'exclude names matching the given patterns'),
101 _('PATTERN'), 101 _(b'PATTERN'),
102 ), 102 ),
103 ] 103 ]
104 104
105 commitopts = [ 105 commitopts = [
106 ('m', 'message', '', _('use text as commit message'), _('TEXT')), 106 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
107 ('l', 'logfile', '', _('read commit message from file'), _('FILE')), 107 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
108 ] 108 ]
109 109
110 commitopts2 = [ 110 commitopts2 = [
111 ('d', 'date', '', _('record the specified date as commit date'), _('DATE')), 111 (
112 ('u', 'user', '', _('record the specified user as committer'), _('USER')), 112 b'd',
113 b'date',
114 b'',
115 _(b'record the specified date as commit date'),
116 _(b'DATE'),
117 ),
118 (
119 b'u',
120 b'user',
121 b'',
122 _(b'record the specified user as committer'),
123 _(b'USER'),
124 ),
113 ] 125 ]
114 126
115 commitopts3 = [ 127 commitopts3 = [
116 (b'D', b'currentdate', None, _(b'record the current date as commit date')), 128 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
117 (b'U', b'currentuser', None, _(b'record the current user as committer')), 129 (b'U', b'currentuser', None, _(b'record the current user as committer')),
118 ] 130 ]
119 131
120 formatteropts = [ 132 formatteropts = [
121 ('T', 'template', '', _('display with template'), _('TEMPLATE')), 133 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
122 ] 134 ]
123 135
124 templateopts = [ 136 templateopts = [
125 ( 137 (
126 '', 138 b'',
127 'style', 139 b'style',
128 '', 140 b'',
129 _('display using template map file (DEPRECATED)'), 141 _(b'display using template map file (DEPRECATED)'),
130 _('STYLE'), 142 _(b'STYLE'),
131 ), 143 ),
132 ('T', 'template', '', _('display with template'), _('TEMPLATE')), 144 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
133 ] 145 ]
134 146
135 logopts = [ 147 logopts = [
136 ('p', 'patch', None, _('show patch')), 148 (b'p', b'patch', None, _(b'show patch')),
137 ('g', 'git', None, _('use git extended diff format')), 149 (b'g', b'git', None, _(b'use git extended diff format')),
138 ('l', 'limit', '', _('limit number of changes displayed'), _('NUM')), 150 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
139 ('M', 'no-merges', None, _('do not show merges')), 151 (b'M', b'no-merges', None, _(b'do not show merges')),
140 ('', 'stat', None, _('output diffstat-style summary of changes')), 152 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
141 ('G', 'graph', None, _("show the revision DAG")), 153 (b'G', b'graph', None, _(b"show the revision DAG")),
142 ] + templateopts 154 ] + templateopts
143 155
144 diffopts = [ 156 diffopts = [
145 ('a', 'text', None, _('treat all files as text')), 157 (b'a', b'text', None, _(b'treat all files as text')),
146 ('g', 'git', None, _('use git extended diff format')), 158 (b'g', b'git', None, _(b'use git extended diff format')),
147 ('', 'binary', None, _('generate binary diffs in git mode (default)')), 159 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
148 ('', 'nodates', None, _('omit dates from diff headers')), 160 (b'', b'nodates', None, _(b'omit dates from diff headers')),
149 ] 161 ]
150 162
151 diffwsopts = [ 163 diffwsopts = [
152 ( 164 (
153 'w', 165 b'w',
154 'ignore-all-space', 166 b'ignore-all-space',
155 None, 167 None,
156 _('ignore white space when comparing lines'), 168 _(b'ignore white space when comparing lines'),
157 ), 169 ),
158 ( 170 (
159 'b', 171 b'b',
160 'ignore-space-change', 172 b'ignore-space-change',
161 None, 173 None,
162 _('ignore changes in the amount of white space'), 174 _(b'ignore changes in the amount of white space'),
163 ), 175 ),
164 ( 176 (
165 'B', 177 b'B',
166 'ignore-blank-lines', 178 b'ignore-blank-lines',
167 None, 179 None,
168 _('ignore changes whose lines are all blank'), 180 _(b'ignore changes whose lines are all blank'),
169 ), 181 ),
170 ( 182 (
171 'Z', 183 b'Z',
172 'ignore-space-at-eol', 184 b'ignore-space-at-eol',
173 None, 185 None,
174 _('ignore changes in whitespace at EOL'), 186 _(b'ignore changes in whitespace at EOL'),
175 ), 187 ),
176 ] 188 ]
177 189
178 diffopts2 = ( 190 diffopts2 = (
179 [ 191 [
180 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')), 192 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
181 ( 193 (
182 'p', 194 b'p',
183 'show-function', 195 b'show-function',
184 None, 196 None,
185 _('show which function each change is in'), 197 _(b'show which function each change is in'),
186 ), 198 ),
187 ('', 'reverse', None, _('produce a diff that undoes the changes')), 199 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
188 ] 200 ]
189 + diffwsopts 201 + diffwsopts
190 + [ 202 + [
191 ('U', 'unified', '', _('number of lines of context to show'), _('NUM')), 203 (
192 ('', 'stat', None, _('output diffstat-style summary of changes')), 204 b'U',
193 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')), 205 b'unified',
206 b'',
207 _(b'number of lines of context to show'),
208 _(b'NUM'),
209 ),
210 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
211 (
212 b'',
213 b'root',
214 b'',
215 _(b'produce diffs relative to subdirectory'),
216 _(b'DIR'),
217 ),
194 ] 218 ]
195 ) 219 )
196 220
197 mergetoolopts = [ 221 mergetoolopts = [
198 ('t', 'tool', '', _('specify merge tool'), _('TOOL')), 222 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
199 ] 223 ]
200 224
201 similarityopts = [ 225 similarityopts = [
202 ( 226 (
203 's', 227 b's',
204 'similarity', 228 b'similarity',
205 '', 229 b'',
206 _('guess renamed files by similarity (0<=s<=100)'), 230 _(b'guess renamed files by similarity (0<=s<=100)'),
207 _('SIMILARITY'), 231 _(b'SIMILARITY'),
208 ) 232 )
209 ] 233 ]
210 234
211 subrepoopts = [('S', 'subrepos', None, _('recurse into subrepositories'))] 235 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
212 236
213 debugrevlogopts = [ 237 debugrevlogopts = [
214 ('c', 'changelog', False, _('open changelog')), 238 (b'c', b'changelog', False, _(b'open changelog')),
215 ('m', 'manifest', False, _('open manifest')), 239 (b'm', b'manifest', False, _(b'open manifest')),
216 ('', 'dir', '', _('open directory manifest')), 240 (b'', b'dir', b'', _(b'open directory manifest')),
217 ] 241 ]
218 242
219 # special string such that everything below this line will be ingored in the 243 # special string such that everything below this line will be ingored in the
220 # editor text 244 # editor text
221 _linebelow = "^HG: ------------------------ >8 ------------------------$" 245 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
222 246
223 247
224 def resolvecommitoptions(ui, opts): 248 def resolvecommitoptions(ui, opts):
225 """modify commit options dict to handle related options 249 """modify commit options dict to handle related options
226 250
227 The return value indicates that ``rewrite.update-timestamp`` is the reason 251 The return value indicates that ``rewrite.update-timestamp`` is the reason
228 the ``date`` option is set. 252 the ``date`` option is set.
229 """ 253 """
230 if opts.get('date') and opts.get('currentdate'): 254 if opts.get(b'date') and opts.get(b'currentdate'):
231 raise error.Abort( 255 raise error.Abort(
232 _('--date and --currentdate are mutually ' 'exclusive') 256 _(b'--date and --currentdate are mutually ' b'exclusive')
233 ) 257 )
234 if opts.get(b'user') and opts.get(b'currentuser'): 258 if opts.get(b'user') and opts.get(b'currentuser'):
235 raise error.Abort( 259 raise error.Abort(
236 _('--user and --currentuser are mutually ' 'exclusive') 260 _(b'--user and --currentuser are mutually ' b'exclusive')
237 ) 261 )
238 262
239 datemaydiffer = False # date-only change should be ignored? 263 datemaydiffer = False # date-only change should be ignored?
240 264
241 if opts.get(b'currentdate'): 265 if opts.get(b'currentdate'):
242 opts[b'date'] = b'%d %d' % dateutil.makedate() 266 opts[b'date'] = b'%d %d' % dateutil.makedate()
243 elif ( 267 elif (
244 not opts.get('date') 268 not opts.get(b'date')
245 and ui.configbool('rewrite', 'update-timestamp') 269 and ui.configbool(b'rewrite', b'update-timestamp')
246 and opts.get('currentdate') is None 270 and opts.get(b'currentdate') is None
247 ): 271 ):
248 opts[b'date'] = b'%d %d' % dateutil.makedate() 272 opts[b'date'] = b'%d %d' % dateutil.makedate()
249 datemaydiffer = True 273 datemaydiffer = True
250 274
251 if opts.get(b'currentuser'): 275 if opts.get(b'currentuser'):
255 279
256 280
257 def checknotesize(ui, opts): 281 def checknotesize(ui, opts):
258 """ make sure note is of valid format """ 282 """ make sure note is of valid format """
259 283
260 note = opts.get('note') 284 note = opts.get(b'note')
261 if not note: 285 if not note:
262 return 286 return
263 287
264 if len(note) > 255: 288 if len(note) > 255:
265 raise error.Abort(_(b"cannot store a note of more than 255 bytes")) 289 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
287 ) 311 )
288 return newlyaddedandmodifiedfiles, alsorestore 312 return newlyaddedandmodifiedfiles, alsorestore
289 313
290 314
291 def parsealiases(cmd): 315 def parsealiases(cmd):
292 return cmd.split("|") 316 return cmd.split(b"|")
293 317
294 318
295 def setupwrapcolorwrite(ui): 319 def setupwrapcolorwrite(ui):
296 # wrap ui.write so diff output can be labeled/colorized 320 # wrap ui.write so diff output can be labeled/colorized
297 def wrapwrite(orig, *args, **kw): 321 def wrapwrite(orig, *args, **kw):
298 label = kw.pop(r'label', '') 322 label = kw.pop(r'label', b'')
299 for chunk, l in patch.difflabel(lambda: args): 323 for chunk, l in patch.difflabel(lambda: args):
300 orig(chunk, label=label + l) 324 orig(chunk, label=label + l)
301 325
302 oldwrite = ui.write 326 oldwrite = ui.write
303 327
320 344
321 return crecordmod.filterpatch( 345 return crecordmod.filterpatch(
322 ui, originalhunks, recordfn, operation 346 ui, originalhunks, recordfn, operation
323 ) 347 )
324 except crecordmod.fallbackerror as e: 348 except crecordmod.fallbackerror as e:
325 ui.warn('%s\n' % e.message) 349 ui.warn(b'%s\n' % e.message)
326 ui.warn(_('falling back to text mode\n')) 350 ui.warn(_(b'falling back to text mode\n'))
327 351
328 return patch.filterpatch(ui, originalhunks, match, operation) 352 return patch.filterpatch(ui, originalhunks, match, operation)
329 353
330 354
331 def recordfilter(ui, originalhunks, match, operation=None): 355 def recordfilter(ui, originalhunks, match, operation=None):
334 *operation* is used for to build ui messages to indicate the user what 358 *operation* is used for to build ui messages to indicate the user what
335 kind of filtering they are doing: reverting, committing, shelving, etc. 359 kind of filtering they are doing: reverting, committing, shelving, etc.
336 (see patch.filterpatch). 360 (see patch.filterpatch).
337 """ 361 """
338 usecurses = crecordmod.checkcurses(ui) 362 usecurses = crecordmod.checkcurses(ui)
339 testfile = ui.config('experimental', 'crecordtest') 363 testfile = ui.config(b'experimental', b'crecordtest')
340 oldwrite = setupwrapcolorwrite(ui) 364 oldwrite = setupwrapcolorwrite(ui)
341 try: 365 try:
342 newchunks, newopts = filterchunks( 366 newchunks, newopts = filterchunks(
343 ui, originalhunks, usecurses, testfile, match, operation 367 ui, originalhunks, usecurses, testfile, match, operation
344 ) 368 )
351 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts 375 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
352 ): 376 ):
353 opts = pycompat.byteskwargs(opts) 377 opts = pycompat.byteskwargs(opts)
354 if not ui.interactive(): 378 if not ui.interactive():
355 if cmdsuggest: 379 if cmdsuggest:
356 msg = _('running non-interactively, use %s instead') % cmdsuggest 380 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
357 else: 381 else:
358 msg = _('running non-interactively') 382 msg = _(b'running non-interactively')
359 raise error.Abort(msg) 383 raise error.Abort(msg)
360 384
361 # make sure username is set before going interactive 385 # make sure username is set before going interactive
362 if not opts.get('user'): 386 if not opts.get(b'user'):
363 ui.username() # raise exception, username not provided 387 ui.username() # raise exception, username not provided
364 388
365 def recordfunc(ui, repo, message, match, opts): 389 def recordfunc(ui, repo, message, match, opts):
366 """This is generic record driver. 390 """This is generic record driver.
367 391
374 working directory is restored to its original state. 398 working directory is restored to its original state.
375 399
376 In the end we'll record interesting changes, and everything else 400 In the end we'll record interesting changes, and everything else
377 will be left in place, so the user can continue working. 401 will be left in place, so the user can continue working.
378 """ 402 """
379 if not opts.get('interactive-unshelve'): 403 if not opts.get(b'interactive-unshelve'):
380 checkunfinished(repo, commit=True) 404 checkunfinished(repo, commit=True)
381 wctx = repo[None] 405 wctx = repo[None]
382 merge = len(wctx.parents()) > 1 406 merge = len(wctx.parents()) > 1
383 if merge: 407 if merge:
384 raise error.Abort( 408 raise error.Abort(
385 _( 409 _(
386 'cannot partially commit a merge ' 410 b'cannot partially commit a merge '
387 '(use "hg commit" instead)' 411 b'(use "hg commit" instead)'
388 ) 412 )
389 ) 413 )
390 414
391 def fail(f, msg): 415 def fail(f, msg):
392 raise error.Abort('%s: %s' % (f, msg)) 416 raise error.Abort(b'%s: %s' % (f, msg))
393 417
394 force = opts.get('force') 418 force = opts.get(b'force')
395 if not force: 419 if not force:
396 vdirs = [] 420 vdirs = []
397 match = matchmod.badmatch(match, fail) 421 match = matchmod.badmatch(match, fail)
398 match.explicitdir = vdirs.append 422 match.explicitdir = vdirs.append
399 423
426 repo.checkcommitpatterns(wctx, vdirs, match, status, fail) 450 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
427 diffopts = patch.difffeatureopts( 451 diffopts = patch.difffeatureopts(
428 ui, 452 ui,
429 opts=opts, 453 opts=opts,
430 whitespace=True, 454 whitespace=True,
431 section='commands', 455 section=b'commands',
432 configprefix='commit.interactive.', 456 configprefix=b'commit.interactive.',
433 ) 457 )
434 diffopts.nodates = True 458 diffopts.nodates = True
435 diffopts.git = True 459 diffopts.git = True
436 diffopts.showfunc = True 460 diffopts.showfunc = True
437 originaldiff = patch.diff(repo, changes=status, opts=diffopts) 461 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
440 464
441 # 1. filter patch, since we are intending to apply subset of it 465 # 1. filter patch, since we are intending to apply subset of it
442 try: 466 try:
443 chunks, newopts = filterfn(ui, originalchunks, match) 467 chunks, newopts = filterfn(ui, originalchunks, match)
444 except error.PatchError as err: 468 except error.PatchError as err:
445 raise error.Abort(_('error parsing patch: %s') % err) 469 raise error.Abort(_(b'error parsing patch: %s') % err)
446 opts.update(newopts) 470 opts.update(newopts)
447 471
448 # We need to keep a backup of files that have been newly added and 472 # We need to keep a backup of files that have been newly added and
449 # modified during the recording process because there is a previous 473 # modified during the recording process because there is a previous
450 # version without the edit in the workdir. We also will need to restore 474 # version without the edit in the workdir. We also will need to restore
461 pass 485 pass
462 486
463 changed = status.modified + status.added + status.removed 487 changed = status.modified + status.added + status.removed
464 newfiles = [f for f in changed if f in contenders] 488 newfiles = [f for f in changed if f in contenders]
465 if not newfiles: 489 if not newfiles:
466 ui.status(_('no changes to record\n')) 490 ui.status(_(b'no changes to record\n'))
467 return 0 491 return 0
468 492
469 modified = set(status.modified) 493 modified = set(status.modified)
470 494
471 # 2. backup changed files, so we can restore them in the end 495 # 2. backup changed files, so we can restore them in the end
478 for f in newfiles 502 for f in newfiles
479 if f in modified or f in newlyaddedandmodifiedfiles 503 if f in modified or f in newlyaddedandmodifiedfiles
480 ] 504 ]
481 backups = {} 505 backups = {}
482 if tobackup: 506 if tobackup:
483 backupdir = repo.vfs.join('record-backups') 507 backupdir = repo.vfs.join(b'record-backups')
484 try: 508 try:
485 os.mkdir(backupdir) 509 os.mkdir(backupdir)
486 except OSError as err: 510 except OSError as err:
487 if err.errno != errno.EEXIST: 511 if err.errno != errno.EEXIST:
488 raise 512 raise
489 try: 513 try:
490 # backup continues 514 # backup continues
491 for f in tobackup: 515 for f in tobackup:
492 fd, tmpname = pycompat.mkstemp( 516 fd, tmpname = pycompat.mkstemp(
493 prefix=f.replace('/', '_') + '.', dir=backupdir 517 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
494 ) 518 )
495 os.close(fd) 519 os.close(fd)
496 ui.debug('backup %r as %r\n' % (f, tmpname)) 520 ui.debug(b'backup %r as %r\n' % (f, tmpname))
497 util.copyfile(repo.wjoin(f), tmpname, copystat=True) 521 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
498 backups[f] = tmpname 522 backups[f] = tmpname
499 523
500 fp = stringio() 524 fp = stringio()
501 for c in chunks: 525 for c in chunks:
504 c.write(fp) 528 c.write(fp)
505 dopatch = fp.tell() 529 dopatch = fp.tell()
506 fp.seek(0) 530 fp.seek(0)
507 531
508 # 2.5 optionally review / modify patch in text editor 532 # 2.5 optionally review / modify patch in text editor
509 if opts.get('review', False): 533 if opts.get(b'review', False):
510 patchtext = ( 534 patchtext = (
511 crecordmod.diffhelptext 535 crecordmod.diffhelptext
512 + crecordmod.patchhelptext 536 + crecordmod.patchhelptext
513 + fp.read() 537 + fp.read()
514 ) 538 )
515 reviewedpatch = ui.edit( 539 reviewedpatch = ui.edit(
516 patchtext, "", action="diff", repopath=repo.path 540 patchtext, b"", action=b"diff", repopath=repo.path
517 ) 541 )
518 fp.truncate(0) 542 fp.truncate(0)
519 fp.write(reviewedpatch) 543 fp.write(reviewedpatch)
520 fp.seek(0) 544 fp.seek(0)
521 545
533 ) 557 )
534 558
535 # 3b. (apply) 559 # 3b. (apply)
536 if dopatch: 560 if dopatch:
537 try: 561 try:
538 ui.debug('applying patch\n') 562 ui.debug(b'applying patch\n')
539 ui.debug(fp.getvalue()) 563 ui.debug(fp.getvalue())
540 patch.internalpatch(ui, repo, fp, 1, eolmode=None) 564 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
541 except error.PatchError as err: 565 except error.PatchError as err:
542 raise error.Abort(pycompat.bytestr(err)) 566 raise error.Abort(pycompat.bytestr(err))
543 del fp 567 del fp
552 finally: 576 finally:
553 # 5. finally restore backed-up files 577 # 5. finally restore backed-up files
554 try: 578 try:
555 dirstate = repo.dirstate 579 dirstate = repo.dirstate
556 for realname, tmpname in backups.iteritems(): 580 for realname, tmpname in backups.iteritems():
557 ui.debug('restoring %r to %r\n' % (tmpname, realname)) 581 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
558 582
559 if dirstate[realname] == 'n': 583 if dirstate[realname] == b'n':
560 # without normallookup, restoring timestamp 584 # without normallookup, restoring timestamp
561 # may cause partially committed files 585 # may cause partially committed files
562 # to be treated as unmodified 586 # to be treated as unmodified
563 dirstate.normallookup(realname) 587 dirstate.normallookup(realname)
564 588
616 there. 640 there.
617 """ 641 """
618 642
619 # the filename contains a path separator, it means it's not the direct 643 # the filename contains a path separator, it means it's not the direct
620 # child of this directory 644 # child of this directory
621 if '/' in filename: 645 if b'/' in filename:
622 subdir, filep = filename.split('/', 1) 646 subdir, filep = filename.split(b'/', 1)
623 647
624 # does the dirnode object for subdir exists 648 # does the dirnode object for subdir exists
625 if subdir not in self.subdirs: 649 if subdir not in self.subdirs:
626 subdirpath = pathutil.join(self.path, subdir) 650 subdirpath = pathutil.join(self.path, subdir)
627 self.subdirs[subdir] = dirnode(subdirpath) 651 self.subdirs[subdir] = dirnode(subdirpath)
667 onlyst = self.statuses.pop() 691 onlyst = self.statuses.pop()
668 692
669 # Making sure we terse only when the status abbreviation is 693 # Making sure we terse only when the status abbreviation is
670 # passed as terse argument 694 # passed as terse argument
671 if onlyst in terseargs: 695 if onlyst in terseargs:
672 yield onlyst, self.path + '/' 696 yield onlyst, self.path + b'/'
673 return 697 return
674 698
675 # add the files to status list 699 # add the files to status list
676 for st, fpath in self.iterfilepaths(): 700 for st, fpath in self.iterfilepaths():
677 yield st, fpath 701 yield st, fpath
694 The function makes a tree of objects of dirnode class, and at each node it 718 The function makes a tree of objects of dirnode class, and at each node it
695 stores the information required to know whether we can terse a certain 719 stores the information required to know whether we can terse a certain
696 directory or not. 720 directory or not.
697 """ 721 """
698 # the order matters here as that is used to produce final list 722 # the order matters here as that is used to produce final list
699 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c') 723 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
700 724
701 # checking the argument validity 725 # checking the argument validity
702 for s in pycompat.bytestr(terseargs): 726 for s in pycompat.bytestr(terseargs):
703 if s not in allst: 727 if s not in allst:
704 raise error.Abort(_("'%s' not recognized") % s) 728 raise error.Abort(_(b"'%s' not recognized") % s)
705 729
706 # creating a dirnode object for the root of the repo 730 # creating a dirnode object for the root of the repo
707 rootobj = dirnode('') 731 rootobj = dirnode(b'')
708 pstatus = ( 732 pstatus = (
709 'modified', 733 b'modified',
710 'added', 734 b'added',
711 'deleted', 735 b'deleted',
712 'clean', 736 b'clean',
713 'unknown', 737 b'unknown',
714 'ignored', 738 b'ignored',
715 'removed', 739 b'removed',
716 ) 740 )
717 741
718 tersedict = {} 742 tersedict = {}
719 for attrname in pstatus: 743 for attrname in pstatus:
720 statuschar = attrname[0:1] 744 statuschar = attrname[0:1]
740 764
741 765
742 def _commentlines(raw): 766 def _commentlines(raw):
743 '''Surround lineswith a comment char and a new line''' 767 '''Surround lineswith a comment char and a new line'''
744 lines = raw.splitlines() 768 lines = raw.splitlines()
745 commentedlines = ['# %s' % line for line in lines] 769 commentedlines = [b'# %s' % line for line in lines]
746 return '\n'.join(commentedlines) + '\n' 770 return b'\n'.join(commentedlines) + b'\n'
747 771
748 772
749 def _conflictsmsg(repo): 773 def _conflictsmsg(repo):
750 mergestate = mergemod.mergestate.read(repo) 774 mergestate = mergemod.mergestate.read(repo)
751 if not mergestate.active(): 775 if not mergestate.active():
752 return 776 return
753 777
754 m = scmutil.match(repo[None]) 778 m = scmutil.match(repo[None])
755 unresolvedlist = [f for f in mergestate.unresolved() if m(f)] 779 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
756 if unresolvedlist: 780 if unresolvedlist:
757 mergeliststr = '\n'.join( 781 mergeliststr = b'\n'.join(
758 [ 782 [
759 ' %s' % util.pathto(repo.root, encoding.getcwd(), path) 783 b' %s' % util.pathto(repo.root, encoding.getcwd(), path)
760 for path in sorted(unresolvedlist) 784 for path in sorted(unresolvedlist)
761 ] 785 ]
762 ) 786 )
763 msg = ( 787 msg = (
764 _( 788 _(
769 To mark files as resolved: hg resolve --mark FILE''' 793 To mark files as resolved: hg resolve --mark FILE'''
770 ) 794 )
771 % mergeliststr 795 % mergeliststr
772 ) 796 )
773 else: 797 else:
774 msg = _('No unresolved merge conflicts.') 798 msg = _(b'No unresolved merge conflicts.')
775 799
776 return _commentlines(msg) 800 return _commentlines(msg)
777 801
778 802
779 def morestatus(repo, fm): 803 def morestatus(repo, fm):
780 statetuple = statemod.getrepostate(repo) 804 statetuple = statemod.getrepostate(repo)
781 label = 'status.morestatus' 805 label = b'status.morestatus'
782 if statetuple: 806 if statetuple:
783 state, helpfulmsg = statetuple 807 state, helpfulmsg = statetuple
784 statemsg = _('The repository is in an unfinished *%s* state.') % state 808 statemsg = _(b'The repository is in an unfinished *%s* state.') % state
785 fm.plain('%s\n' % _commentlines(statemsg), label=label) 809 fm.plain(b'%s\n' % _commentlines(statemsg), label=label)
786 conmsg = _conflictsmsg(repo) 810 conmsg = _conflictsmsg(repo)
787 if conmsg: 811 if conmsg:
788 fm.plain('%s\n' % conmsg, label=label) 812 fm.plain(b'%s\n' % conmsg, label=label)
789 if helpfulmsg: 813 if helpfulmsg:
790 fm.plain('%s\n' % _commentlines(helpfulmsg), label=label) 814 fm.plain(b'%s\n' % _commentlines(helpfulmsg), label=label)
791 815
792 816
793 def findpossible(cmd, table, strict=False): 817 def findpossible(cmd, table, strict=False):
794 """ 818 """
795 Return cmd -> (aliases, command table entry) 819 Return cmd -> (aliases, command table entry)
816 for a in aliases: 840 for a in aliases:
817 if a.startswith(cmd): 841 if a.startswith(cmd):
818 found = a 842 found = a
819 break 843 break
820 if found is not None: 844 if found is not None:
821 if aliases[0].startswith("debug") or found.startswith("debug"): 845 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
822 debugchoice[found] = (aliases, table[e]) 846 debugchoice[found] = (aliases, table[e])
823 else: 847 else:
824 choice[found] = (aliases, table[e]) 848 choice[found] = (aliases, table[e])
825 849
826 if not choice and debugchoice: 850 if not choice and debugchoice:
847 871
848 872
849 def changebranch(ui, repo, revs, label): 873 def changebranch(ui, repo, revs, label):
850 """ Change the branch name of given revs to label """ 874 """ Change the branch name of given revs to label """
851 875
852 with repo.wlock(), repo.lock(), repo.transaction('branches'): 876 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
853 # abort in case of uncommitted merge or dirty wdir 877 # abort in case of uncommitted merge or dirty wdir
854 bailifchanged(repo) 878 bailifchanged(repo)
855 revs = scmutil.revrange(repo, revs) 879 revs = scmutil.revrange(repo, revs)
856 if not revs: 880 if not revs:
857 raise error.Abort("empty revision set") 881 raise error.Abort(b"empty revision set")
858 roots = repo.revs('roots(%ld)', revs) 882 roots = repo.revs(b'roots(%ld)', revs)
859 if len(roots) > 1: 883 if len(roots) > 1:
860 raise error.Abort(_("cannot change branch of non-linear revisions")) 884 raise error.Abort(
861 rewriteutil.precheck(repo, revs, 'change branch of') 885 _(b"cannot change branch of non-linear revisions")
886 )
887 rewriteutil.precheck(repo, revs, b'change branch of')
862 888
863 root = repo[roots.first()] 889 root = repo[roots.first()]
864 rpb = {parent.branch() for parent in root.parents()} 890 rpb = {parent.branch() for parent in root.parents()}
865 if label not in rpb and label in repo.branchmap(): 891 if label not in rpb and label in repo.branchmap():
866 raise error.Abort(_("a branch of the same name already exists")) 892 raise error.Abort(_(b"a branch of the same name already exists"))
867 893
868 if repo.revs('obsolete() and %ld', revs): 894 if repo.revs(b'obsolete() and %ld', revs):
869 raise error.Abort(_("cannot change branch of a obsolete changeset")) 895 raise error.Abort(
896 _(b"cannot change branch of a obsolete changeset")
897 )
870 898
871 # make sure only topological heads 899 # make sure only topological heads
872 if repo.revs('heads(%ld) - head()', revs): 900 if repo.revs(b'heads(%ld) - head()', revs):
873 raise error.Abort(_("cannot change branch in middle of a stack")) 901 raise error.Abort(_(b"cannot change branch in middle of a stack"))
874 902
875 replacements = {} 903 replacements = {}
876 # avoid import cycle mercurial.cmdutil -> mercurial.context -> 904 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
877 # mercurial.subrepo -> mercurial.cmdutil 905 # mercurial.subrepo -> mercurial.cmdutil
878 from . import context 906 from . import context
889 return ctx[path] 917 return ctx[path]
890 except error.ManifestLookupError: 918 except error.ManifestLookupError:
891 return None 919 return None
892 920
893 ui.debug( 921 ui.debug(
894 "changing branch of '%s' from '%s' to '%s'\n" 922 b"changing branch of '%s' from '%s' to '%s'\n"
895 % (hex(ctx.node()), oldbranch, label) 923 % (hex(ctx.node()), oldbranch, label)
896 ) 924 )
897 extra = ctx.extra() 925 extra = ctx.extra()
898 extra['branch_change'] = hex(ctx.node()) 926 extra[b'branch_change'] = hex(ctx.node())
899 # While changing branch of set of linear commits, make sure that 927 # While changing branch of set of linear commits, make sure that
900 # we base our commits on new parent rather than old parent which 928 # we base our commits on new parent rather than old parent which
901 # was obsoleted while changing the branch 929 # was obsoleted while changing the branch
902 p1 = ctx.p1().node() 930 p1 = ctx.p1().node()
903 p2 = ctx.p2().node() 931 p2 = ctx.p2().node()
918 branch=label, 946 branch=label,
919 ) 947 )
920 948
921 newnode = repo.commitctx(mc) 949 newnode = repo.commitctx(mc)
922 replacements[ctx.node()] = (newnode,) 950 replacements[ctx.node()] = (newnode,)
923 ui.debug('new node id is %s\n' % hex(newnode)) 951 ui.debug(b'new node id is %s\n' % hex(newnode))
924 952
925 # create obsmarkers and move bookmarks 953 # create obsmarkers and move bookmarks
926 scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True) 954 scmutil.cleanupnodes(
955 repo, replacements, b'branch-change', fixphase=True
956 )
927 957
928 # move the working copy too 958 # move the working copy too
929 wctx = repo[None] 959 wctx = repo[None]
930 # in-progress merge is a bit too complex for now. 960 # in-progress merge is a bit too complex for now.
931 if len(wctx.parents()) == 1: 961 if len(wctx.parents()) == 1:
935 # mercurial.cmdutil 965 # mercurial.cmdutil
936 from . import hg 966 from . import hg
937 967
938 hg.update(repo, newid[0], quietempty=True) 968 hg.update(repo, newid[0], quietempty=True)
939 969
940 ui.status(_("changed branch on %d changesets\n") % len(replacements)) 970 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
941 971
942 972
943 def findrepo(p): 973 def findrepo(p):
944 while not os.path.isdir(os.path.join(p, ".hg")): 974 while not os.path.isdir(os.path.join(p, b".hg")):
945 oldp, p = p, os.path.dirname(p) 975 oldp, p = p, os.path.dirname(p)
946 if p == oldp: 976 if p == oldp:
947 return None 977 return None
948 978
949 return p 979 return p
957 987
958 'hint' is the usual hint given to Abort exception. 988 'hint' is the usual hint given to Abort exception.
959 """ 989 """
960 990
961 if merge and repo.dirstate.p2() != nullid: 991 if merge and repo.dirstate.p2() != nullid:
962 raise error.Abort(_('outstanding uncommitted merge'), hint=hint) 992 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
963 modified, added, removed, deleted = repo.status()[:4] 993 modified, added, removed, deleted = repo.status()[:4]
964 if modified or added or removed or deleted: 994 if modified or added or removed or deleted:
965 raise error.Abort(_('uncommitted changes'), hint=hint) 995 raise error.Abort(_(b'uncommitted changes'), hint=hint)
966 ctx = repo[None] 996 ctx = repo[None]
967 for s in sorted(ctx.substate): 997 for s in sorted(ctx.substate):
968 ctx.sub(s).bailifchanged(hint=hint) 998 ctx.sub(s).bailifchanged(hint=hint)
969 999
970 1000
971 def logmessage(ui, opts): 1001 def logmessage(ui, opts):
972 """ get the log message according to -m and -l option """ 1002 """ get the log message according to -m and -l option """
973 message = opts.get('message') 1003 message = opts.get(b'message')
974 logfile = opts.get('logfile') 1004 logfile = opts.get(b'logfile')
975 1005
976 if message and logfile: 1006 if message and logfile:
977 raise error.Abort( 1007 raise error.Abort(
978 _('options --message and --logfile are mutually ' 'exclusive') 1008 _(b'options --message and --logfile are mutually ' b'exclusive')
979 ) 1009 )
980 if not message and logfile: 1010 if not message and logfile:
981 try: 1011 try:
982 if isstdiofilename(logfile): 1012 if isstdiofilename(logfile):
983 message = ui.fin.read() 1013 message = ui.fin.read()
984 else: 1014 else:
985 message = '\n'.join(util.readfile(logfile).splitlines()) 1015 message = b'\n'.join(util.readfile(logfile).splitlines())
986 except IOError as inst: 1016 except IOError as inst:
987 raise error.Abort( 1017 raise error.Abort(
988 _("can't read commit message '%s': %s") 1018 _(b"can't read commit message '%s': %s")
989 % (logfile, encoding.strtolocal(inst.strerror)) 1019 % (logfile, encoding.strtolocal(inst.strerror))
990 ) 1020 )
991 return message 1021 return message
992 1022
993 1023
1000 This returns baseformname with '.merge' appended if it is a merge, 1030 This returns baseformname with '.merge' appended if it is a merge,
1001 otherwise '.normal' is appended. 1031 otherwise '.normal' is appended.
1002 """ 1032 """
1003 if isinstance(ctxorbool, bool): 1033 if isinstance(ctxorbool, bool):
1004 if ctxorbool: 1034 if ctxorbool:
1005 return baseformname + ".merge" 1035 return baseformname + b".merge"
1006 elif len(ctxorbool.parents()) > 1: 1036 elif len(ctxorbool.parents()) > 1:
1007 return baseformname + ".merge" 1037 return baseformname + b".merge"
1008 1038
1009 return baseformname + ".normal" 1039 return baseformname + b".normal"
1010 1040
1011 1041
1012 def getcommiteditor( 1042 def getcommiteditor(
1013 edit=False, finishdesc=None, extramsg=None, editform='', **opts 1043 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1014 ): 1044 ):
1015 """get appropriate commit message editor according to '--edit' option 1045 """get appropriate commit message editor according to '--edit' option
1016 1046
1017 'finishdesc' is a function to be called with edited commit message 1047 'finishdesc' is a function to be called with edited commit message
1018 (= 'description' of the new changeset) just after editing, but 1048 (= 'description' of the new changeset) just after editing, but
1079 repo = ctx.repo() 1109 repo = ctx.repo()
1080 tres = formatter.templateresources(repo.ui, repo) 1110 tres = formatter.templateresources(repo.ui, repo)
1081 t = formatter.maketemplater( 1111 t = formatter.maketemplater(
1082 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres 1112 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1083 ) 1113 )
1084 mapping = {'ctx': ctx} 1114 mapping = {b'ctx': ctx}
1085 if props: 1115 if props:
1086 mapping.update(props) 1116 mapping.update(props)
1087 return t.renderdefault(mapping) 1117 return t.renderdefault(mapping)
1088 1118
1089 1119
1143 newname.append(stringutil.escapestr(pat[i:end])) 1173 newname.append(stringutil.escapestr(pat[i:end]))
1144 break 1174 break
1145 newname.append(stringutil.escapestr(pat[i:n])) 1175 newname.append(stringutil.escapestr(pat[i:n]))
1146 if n + 2 > end: 1176 if n + 2 > end:
1147 raise error.Abort( 1177 raise error.Abort(
1148 _("incomplete format spec in output " "filename") 1178 _(b"incomplete format spec in output " b"filename")
1149 ) 1179 )
1150 c = pat[n + 1 : n + 2] 1180 c = pat[n + 1 : n + 2]
1151 i = n + 2 1181 i = n + 2
1152 try: 1182 try:
1153 newname.append(expander[c]) 1183 newname.append(expander[c])
1154 except KeyError: 1184 except KeyError:
1155 raise error.Abort( 1185 raise error.Abort(
1156 _("invalid format spec '%%%s' in output " "filename") % c 1186 _(b"invalid format spec '%%%s' in output " b"filename") % c
1157 ) 1187 )
1158 return ''.join(newname) 1188 return b''.join(newname)
1159 1189
1160 1190
1161 def makefilename(ctx, pat, **props): 1191 def makefilename(ctx, pat, **props):
1162 if not pat: 1192 if not pat:
1163 return pat 1193 return pat
1168 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props)) 1198 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1169 1199
1170 1200
1171 def isstdiofilename(pat): 1201 def isstdiofilename(pat):
1172 """True if the given pat looks like a filename denoting stdin/stdout""" 1202 """True if the given pat looks like a filename denoting stdin/stdout"""
1173 return not pat or pat == '-' 1203 return not pat or pat == b'-'
1174 1204
1175 1205
1176 class _unclosablefile(object): 1206 class _unclosablefile(object):
1177 def __init__(self, fp): 1207 def __init__(self, fp):
1178 self._fp = fp 1208 self._fp = fp
1191 1221
1192 def __exit__(self, exc_type, exc_value, exc_tb): 1222 def __exit__(self, exc_type, exc_value, exc_tb):
1193 pass 1223 pass
1194 1224
1195 1225
1196 def makefileobj(ctx, pat, mode='wb', **props): 1226 def makefileobj(ctx, pat, mode=b'wb', **props):
1197 writable = mode not in ('r', 'rb') 1227 writable = mode not in (b'r', b'rb')
1198 1228
1199 if isstdiofilename(pat): 1229 if isstdiofilename(pat):
1200 repo = ctx.repo() 1230 repo = ctx.repo()
1201 if writable: 1231 if writable:
1202 fp = repo.ui.fout 1232 fp = repo.ui.fout
1207 return open(fn, mode) 1237 return open(fn, mode)
1208 1238
1209 1239
1210 def openstorage(repo, cmd, file_, opts, returnrevlog=False): 1240 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1211 """opens the changelog, manifest, a filelog or a given revlog""" 1241 """opens the changelog, manifest, a filelog or a given revlog"""
1212 cl = opts['changelog'] 1242 cl = opts[b'changelog']
1213 mf = opts['manifest'] 1243 mf = opts[b'manifest']
1214 dir = opts['dir'] 1244 dir = opts[b'dir']
1215 msg = None 1245 msg = None
1216 if cl and mf: 1246 if cl and mf:
1217 msg = _('cannot specify --changelog and --manifest at the same time') 1247 msg = _(b'cannot specify --changelog and --manifest at the same time')
1218 elif cl and dir: 1248 elif cl and dir:
1219 msg = _('cannot specify --changelog and --dir at the same time') 1249 msg = _(b'cannot specify --changelog and --dir at the same time')
1220 elif cl or mf or dir: 1250 elif cl or mf or dir:
1221 if file_: 1251 if file_:
1222 msg = _('cannot specify filename with --changelog or --manifest') 1252 msg = _(b'cannot specify filename with --changelog or --manifest')
1223 elif not repo: 1253 elif not repo:
1224 msg = _( 1254 msg = _(
1225 'cannot specify --changelog or --manifest or --dir ' 1255 b'cannot specify --changelog or --manifest or --dir '
1226 'without a repository' 1256 b'without a repository'
1227 ) 1257 )
1228 if msg: 1258 if msg:
1229 raise error.Abort(msg) 1259 raise error.Abort(msg)
1230 1260
1231 r = None 1261 r = None
1232 if repo: 1262 if repo:
1233 if cl: 1263 if cl:
1234 r = repo.unfiltered().changelog 1264 r = repo.unfiltered().changelog
1235 elif dir: 1265 elif dir:
1236 if 'treemanifest' not in repo.requirements: 1266 if b'treemanifest' not in repo.requirements:
1237 raise error.Abort( 1267 raise error.Abort(
1238 _( 1268 _(
1239 "--dir can only be used on repos with " 1269 b"--dir can only be used on repos with "
1240 "treemanifest enabled" 1270 b"treemanifest enabled"
1241 ) 1271 )
1242 ) 1272 )
1243 if not dir.endswith('/'): 1273 if not dir.endswith(b'/'):
1244 dir = dir + '/' 1274 dir = dir + b'/'
1245 dirlog = repo.manifestlog.getstorage(dir) 1275 dirlog = repo.manifestlog.getstorage(dir)
1246 if len(dirlog): 1276 if len(dirlog):
1247 r = dirlog 1277 r = dirlog
1248 elif mf: 1278 elif mf:
1249 r = repo.manifestlog.getstorage(b'') 1279 r = repo.manifestlog.getstorage(b'')
1255 # Not all storage may be revlogs. If requested, try to return an actual 1285 # Not all storage may be revlogs. If requested, try to return an actual
1256 # revlog instance. 1286 # revlog instance.
1257 if returnrevlog: 1287 if returnrevlog:
1258 if isinstance(r, revlog.revlog): 1288 if isinstance(r, revlog.revlog):
1259 pass 1289 pass
1260 elif util.safehasattr(r, '_revlog'): 1290 elif util.safehasattr(r, b'_revlog'):
1261 r = r._revlog 1291 r = r._revlog
1262 elif r is not None: 1292 elif r is not None:
1263 raise error.Abort(_('%r does not appear to be a revlog') % r) 1293 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1264 1294
1265 if not r: 1295 if not r:
1266 if not returnrevlog: 1296 if not returnrevlog:
1267 raise error.Abort(_('cannot give path to non-revlog')) 1297 raise error.Abort(_(b'cannot give path to non-revlog'))
1268 1298
1269 if not file_: 1299 if not file_:
1270 raise error.CommandError(cmd, _('invalid arguments')) 1300 raise error.CommandError(cmd, _(b'invalid arguments'))
1271 if not os.path.isfile(file_): 1301 if not os.path.isfile(file_):
1272 raise error.Abort(_("revlog '%s' not found") % file_) 1302 raise error.Abort(_(b"revlog '%s' not found") % file_)
1273 r = revlog.revlog( 1303 r = revlog.revlog(
1274 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + ".i" 1304 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1275 ) 1305 )
1276 return r 1306 return r
1277 1307
1278 1308
1279 def openrevlog(repo, cmd, file_, opts): 1309 def openrevlog(repo, cmd, file_, opts):
1294 # 1324 #
1295 # hgsep => pathname that uses "/" to separate directories 1325 # hgsep => pathname that uses "/" to separate directories
1296 # ossep => pathname that uses os.sep to separate directories 1326 # ossep => pathname that uses os.sep to separate directories
1297 cwd = repo.getcwd() 1327 cwd = repo.getcwd()
1298 targets = {} 1328 targets = {}
1299 after = opts.get("after") 1329 after = opts.get(b"after")
1300 dryrun = opts.get("dry_run") 1330 dryrun = opts.get(b"dry_run")
1301 wctx = repo[None] 1331 wctx = repo[None]
1302 1332
1303 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) 1333 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1304 1334
1305 def walkpat(pat): 1335 def walkpat(pat):
1306 srcs = [] 1336 srcs = []
1307 if after: 1337 if after:
1308 badstates = '?' 1338 badstates = b'?'
1309 else: 1339 else:
1310 badstates = '?r' 1340 badstates = b'?r'
1311 m = scmutil.match(wctx, [pat], opts, globbed=True) 1341 m = scmutil.match(wctx, [pat], opts, globbed=True)
1312 for abs in wctx.walk(m): 1342 for abs in wctx.walk(m):
1313 state = repo.dirstate[abs] 1343 state = repo.dirstate[abs]
1314 rel = uipathfn(abs) 1344 rel = uipathfn(abs)
1315 exact = m.exact(abs) 1345 exact = m.exact(abs)
1316 if state in badstates: 1346 if state in badstates:
1317 if exact and state == '?': 1347 if exact and state == b'?':
1318 ui.warn(_('%s: not copying - file is not managed\n') % rel) 1348 ui.warn(_(b'%s: not copying - file is not managed\n') % rel)
1319 if exact and state == 'r': 1349 if exact and state == b'r':
1320 ui.warn( 1350 ui.warn(
1321 _( 1351 _(
1322 '%s: not copying - file has been marked for' 1352 b'%s: not copying - file has been marked for'
1323 ' remove\n' 1353 b' remove\n'
1324 ) 1354 )
1325 % rel 1355 % rel
1326 ) 1356 )
1327 continue 1357 continue
1328 # abs: hgsep 1358 # abs: hgsep
1333 # abssrc: hgsep 1363 # abssrc: hgsep
1334 # relsrc: ossep 1364 # relsrc: ossep
1335 # otarget: ossep 1365 # otarget: ossep
1336 def copyfile(abssrc, relsrc, otarget, exact): 1366 def copyfile(abssrc, relsrc, otarget, exact):
1337 abstarget = pathutil.canonpath(repo.root, cwd, otarget) 1367 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1338 if '/' in abstarget: 1368 if b'/' in abstarget:
1339 # We cannot normalize abstarget itself, this would prevent 1369 # We cannot normalize abstarget itself, this would prevent
1340 # case only renames, like a => A. 1370 # case only renames, like a => A.
1341 abspath, absname = abstarget.rsplit('/', 1) 1371 abspath, absname = abstarget.rsplit(b'/', 1)
1342 abstarget = repo.dirstate.normalize(abspath) + '/' + absname 1372 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1343 reltarget = repo.pathto(abstarget, cwd) 1373 reltarget = repo.pathto(abstarget, cwd)
1344 target = repo.wjoin(abstarget) 1374 target = repo.wjoin(abstarget)
1345 src = repo.wjoin(abssrc) 1375 src = repo.wjoin(abssrc)
1346 state = repo.dirstate[abstarget] 1376 state = repo.dirstate[abstarget]
1347 1377
1349 1379
1350 # check for collisions 1380 # check for collisions
1351 prevsrc = targets.get(abstarget) 1381 prevsrc = targets.get(abstarget)
1352 if prevsrc is not None: 1382 if prevsrc is not None:
1353 ui.warn( 1383 ui.warn(
1354 _('%s: not overwriting - %s collides with %s\n') 1384 _(b'%s: not overwriting - %s collides with %s\n')
1355 % ( 1385 % (
1356 reltarget, 1386 reltarget,
1357 repo.pathto(abssrc, cwd), 1387 repo.pathto(abssrc, cwd),
1358 repo.pathto(prevsrc, cwd), 1388 repo.pathto(prevsrc, cwd),
1359 ) 1389 )
1366 if exists and abssrc != abstarget: 1396 if exists and abssrc != abstarget:
1367 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize( 1397 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1368 abstarget 1398 abstarget
1369 ): 1399 ):
1370 if not rename: 1400 if not rename:
1371 ui.warn(_("%s: can't copy - same file\n") % reltarget) 1401 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1372 return True # report a failure 1402 return True # report a failure
1373 exists = False 1403 exists = False
1374 samefile = True 1404 samefile = True
1375 1405
1376 if not after and exists or after and state in 'mn': 1406 if not after and exists or after and state in b'mn':
1377 if not opts['force']: 1407 if not opts[b'force']:
1378 if state in 'mn': 1408 if state in b'mn':
1379 msg = _('%s: not overwriting - file already committed\n') 1409 msg = _(b'%s: not overwriting - file already committed\n')
1380 if after: 1410 if after:
1381 flags = '--after --force' 1411 flags = b'--after --force'
1382 else: 1412 else:
1383 flags = '--force' 1413 flags = b'--force'
1384 if rename: 1414 if rename:
1385 hint = ( 1415 hint = (
1386 _( 1416 _(
1387 "('hg rename %s' to replace the file by " 1417 b"('hg rename %s' to replace the file by "
1388 'recording a rename)\n' 1418 b'recording a rename)\n'
1389 ) 1419 )
1390 % flags 1420 % flags
1391 ) 1421 )
1392 else: 1422 else:
1393 hint = ( 1423 hint = (
1394 _( 1424 _(
1395 "('hg copy %s' to replace the file by " 1425 b"('hg copy %s' to replace the file by "
1396 'recording a copy)\n' 1426 b'recording a copy)\n'
1397 ) 1427 )
1398 % flags 1428 % flags
1399 ) 1429 )
1400 else: 1430 else:
1401 msg = _('%s: not overwriting - file exists\n') 1431 msg = _(b'%s: not overwriting - file exists\n')
1402 if rename: 1432 if rename:
1403 hint = _("('hg rename --after' to record the rename)\n") 1433 hint = _(
1434 b"('hg rename --after' to record the rename)\n"
1435 )
1404 else: 1436 else:
1405 hint = _("('hg copy --after' to record the copy)\n") 1437 hint = _(b"('hg copy --after' to record the copy)\n")
1406 ui.warn(msg % reltarget) 1438 ui.warn(msg % reltarget)
1407 ui.warn(hint) 1439 ui.warn(hint)
1408 return True # report a failure 1440 return True # report a failure
1409 1441
1410 if after: 1442 if after:
1411 if not exists: 1443 if not exists:
1412 if rename: 1444 if rename:
1413 ui.warn( 1445 ui.warn(
1414 _('%s: not recording move - %s does not exist\n') 1446 _(b'%s: not recording move - %s does not exist\n')
1415 % (relsrc, reltarget) 1447 % (relsrc, reltarget)
1416 ) 1448 )
1417 else: 1449 else:
1418 ui.warn( 1450 ui.warn(
1419 _('%s: not recording copy - %s does not exist\n') 1451 _(b'%s: not recording copy - %s does not exist\n')
1420 % (relsrc, reltarget) 1452 % (relsrc, reltarget)
1421 ) 1453 )
1422 return True # report a failure 1454 return True # report a failure
1423 elif not dryrun: 1455 elif not dryrun:
1424 try: 1456 try:
1425 if exists: 1457 if exists:
1426 os.unlink(target) 1458 os.unlink(target)
1427 targetdir = os.path.dirname(target) or '.' 1459 targetdir = os.path.dirname(target) or b'.'
1428 if not os.path.isdir(targetdir): 1460 if not os.path.isdir(targetdir):
1429 os.makedirs(targetdir) 1461 os.makedirs(targetdir)
1430 if samefile: 1462 if samefile:
1431 tmp = target + "~hgrename" 1463 tmp = target + b"~hgrename"
1432 os.rename(src, tmp) 1464 os.rename(src, tmp)
1433 os.rename(tmp, target) 1465 os.rename(tmp, target)
1434 else: 1466 else:
1435 # Preserve stat info on renames, not on copies; this matches 1467 # Preserve stat info on renames, not on copies; this matches
1436 # Linux CLI behavior. 1468 # Linux CLI behavior.
1437 util.copyfile(src, target, copystat=rename) 1469 util.copyfile(src, target, copystat=rename)
1438 srcexists = True 1470 srcexists = True
1439 except IOError as inst: 1471 except IOError as inst:
1440 if inst.errno == errno.ENOENT: 1472 if inst.errno == errno.ENOENT:
1441 ui.warn(_('%s: deleted in working directory\n') % relsrc) 1473 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1442 srcexists = False 1474 srcexists = False
1443 else: 1475 else:
1444 ui.warn( 1476 ui.warn(
1445 _('%s: cannot copy - %s\n') 1477 _(b'%s: cannot copy - %s\n')
1446 % (relsrc, encoding.strtolocal(inst.strerror)) 1478 % (relsrc, encoding.strtolocal(inst.strerror))
1447 ) 1479 )
1448 return True # report a failure 1480 return True # report a failure
1449 1481
1450 if ui.verbose or not exact: 1482 if ui.verbose or not exact:
1451 if rename: 1483 if rename:
1452 ui.status(_('moving %s to %s\n') % (relsrc, reltarget)) 1484 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1453 else: 1485 else:
1454 ui.status(_('copying %s to %s\n') % (relsrc, reltarget)) 1486 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1455 1487
1456 targets[abstarget] = abssrc 1488 targets[abstarget] = abssrc
1457 1489
1458 # fix up dirstate 1490 # fix up dirstate
1459 scmutil.dirstatecopy( 1491 scmutil.dirstatecopy(
1460 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd 1492 ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1461 ) 1493 )
1462 if rename and not dryrun: 1494 if rename and not dryrun:
1463 if not after and srcexists and not samefile: 1495 if not after and srcexists and not samefile:
1464 rmdir = repo.ui.configbool('experimental', 'removeemptydirs') 1496 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1465 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir) 1497 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1466 wctx.forget([abssrc]) 1498 wctx.forget([abssrc])
1467 1499
1468 # pat: ossep 1500 # pat: ossep
1469 # dest ossep 1501 # dest ossep
1533 res = lambda p: dest 1565 res = lambda p: dest
1534 return res 1566 return res
1535 1567
1536 pats = scmutil.expandpats(pats) 1568 pats = scmutil.expandpats(pats)
1537 if not pats: 1569 if not pats:
1538 raise error.Abort(_('no source or destination specified')) 1570 raise error.Abort(_(b'no source or destination specified'))
1539 if len(pats) == 1: 1571 if len(pats) == 1:
1540 raise error.Abort(_('no destination specified')) 1572 raise error.Abort(_(b'no destination specified'))
1541 dest = pats.pop() 1573 dest = pats.pop()
1542 destdirexists = os.path.isdir(dest) and not os.path.islink(dest) 1574 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1543 if not destdirexists: 1575 if not destdirexists:
1544 if len(pats) > 1 or matchmod.patkind(pats[0]): 1576 if len(pats) > 1 or matchmod.patkind(pats[0]):
1545 raise error.Abort( 1577 raise error.Abort(
1546 _( 1578 _(
1547 'with multiple sources, destination must be an ' 1579 b'with multiple sources, destination must be an '
1548 'existing directory' 1580 b'existing directory'
1549 ) 1581 )
1550 ) 1582 )
1551 if util.endswithsep(dest): 1583 if util.endswithsep(dest):
1552 raise error.Abort(_('destination %s is not a directory') % dest) 1584 raise error.Abort(_(b'destination %s is not a directory') % dest)
1553 1585
1554 tfn = targetpathfn 1586 tfn = targetpathfn
1555 if after: 1587 if after:
1556 tfn = targetpathafterfn 1588 tfn = targetpathafterfn
1557 copylist = [] 1589 copylist = []
1559 srcs = walkpat(pat) 1591 srcs = walkpat(pat)
1560 if not srcs: 1592 if not srcs:
1561 continue 1593 continue
1562 copylist.append((tfn(pat, dest, srcs), srcs)) 1594 copylist.append((tfn(pat, dest, srcs), srcs))
1563 if not copylist: 1595 if not copylist:
1564 raise error.Abort(_('no files to copy')) 1596 raise error.Abort(_(b'no files to copy'))
1565 1597
1566 errors = 0 1598 errors = 0
1567 for targetpath, srcs in copylist: 1599 for targetpath, srcs in copylist:
1568 for abssrc, relsrc, exact in srcs: 1600 for abssrc, relsrc, exact in srcs:
1569 if copyfile(abssrc, relsrc, targetpath(abssrc), exact): 1601 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1613 updatefunc(<repo>, <node>) 1645 updatefunc(<repo>, <node>)
1614 """ 1646 """
1615 # avoid cycle context -> subrepo -> cmdutil 1647 # avoid cycle context -> subrepo -> cmdutil
1616 from . import context 1648 from . import context
1617 1649
1618 tmpname = patchdata.get('filename') 1650 tmpname = patchdata.get(b'filename')
1619 message = patchdata.get('message') 1651 message = patchdata.get(b'message')
1620 user = opts.get('user') or patchdata.get('user') 1652 user = opts.get(b'user') or patchdata.get(b'user')
1621 date = opts.get('date') or patchdata.get('date') 1653 date = opts.get(b'date') or patchdata.get(b'date')
1622 branch = patchdata.get('branch') 1654 branch = patchdata.get(b'branch')
1623 nodeid = patchdata.get('nodeid') 1655 nodeid = patchdata.get(b'nodeid')
1624 p1 = patchdata.get('p1') 1656 p1 = patchdata.get(b'p1')
1625 p2 = patchdata.get('p2') 1657 p2 = patchdata.get(b'p2')
1626 1658
1627 nocommit = opts.get('no_commit') 1659 nocommit = opts.get(b'no_commit')
1628 importbranch = opts.get('import_branch') 1660 importbranch = opts.get(b'import_branch')
1629 update = not opts.get('bypass') 1661 update = not opts.get(b'bypass')
1630 strip = opts["strip"] 1662 strip = opts[b"strip"]
1631 prefix = opts["prefix"] 1663 prefix = opts[b"prefix"]
1632 sim = float(opts.get('similarity') or 0) 1664 sim = float(opts.get(b'similarity') or 0)
1633 1665
1634 if not tmpname: 1666 if not tmpname:
1635 return None, None, False 1667 return None, None, False
1636 1668
1637 rejects = False 1669 rejects = False
1644 # pickup the patch msg 1676 # pickup the patch msg
1645 message = message.strip() 1677 message = message.strip()
1646 else: 1678 else:
1647 # launch the editor 1679 # launch the editor
1648 message = None 1680 message = None
1649 ui.debug('message:\n%s\n' % (message or '')) 1681 ui.debug(b'message:\n%s\n' % (message or b''))
1650 1682
1651 if len(parents) == 1: 1683 if len(parents) == 1:
1652 parents.append(repo[nullid]) 1684 parents.append(repo[nullid])
1653 if opts.get('exact'): 1685 if opts.get(b'exact'):
1654 if not nodeid or not p1: 1686 if not nodeid or not p1:
1655 raise error.Abort(_('not a Mercurial patch')) 1687 raise error.Abort(_(b'not a Mercurial patch'))
1656 p1 = repo[p1] 1688 p1 = repo[p1]
1657 p2 = repo[p2 or nullid] 1689 p2 = repo[p2 or nullid]
1658 elif p2: 1690 elif p2:
1659 try: 1691 try:
1660 p1 = repo[p1] 1692 p1 = repo[p1]
1668 except error.RepoError: 1700 except error.RepoError:
1669 p1, p2 = parents 1701 p1, p2 = parents
1670 if p2.node() == nullid: 1702 if p2.node() == nullid:
1671 ui.warn( 1703 ui.warn(
1672 _( 1704 _(
1673 "warning: import the patch as a normal revision\n" 1705 b"warning: import the patch as a normal revision\n"
1674 "(use --exact to import the patch as a merge)\n" 1706 b"(use --exact to import the patch as a merge)\n"
1675 ) 1707 )
1676 ) 1708 )
1677 else: 1709 else:
1678 p1, p2 = parents 1710 p1, p2 = parents
1679 1711
1682 if p1 != parents[0]: 1714 if p1 != parents[0]:
1683 updatefunc(repo, p1.node()) 1715 updatefunc(repo, p1.node())
1684 if p2 != parents[1]: 1716 if p2 != parents[1]:
1685 repo.setparents(p1.node(), p2.node()) 1717 repo.setparents(p1.node(), p2.node())
1686 1718
1687 if opts.get('exact') or importbranch: 1719 if opts.get(b'exact') or importbranch:
1688 repo.dirstate.setbranch(branch or 'default') 1720 repo.dirstate.setbranch(branch or b'default')
1689 1721
1690 partial = opts.get('partial', False) 1722 partial = opts.get(b'partial', False)
1691 files = set() 1723 files = set()
1692 try: 1724 try:
1693 patch.patch( 1725 patch.patch(
1694 ui, 1726 ui,
1695 repo, 1727 repo,
1709 files = list(files) 1741 files = list(files)
1710 if nocommit: 1742 if nocommit:
1711 if message: 1743 if message:
1712 msgs.append(message) 1744 msgs.append(message)
1713 else: 1745 else:
1714 if opts.get('exact') or p2: 1746 if opts.get(b'exact') or p2:
1715 # If you got here, you either use --force and know what 1747 # If you got here, you either use --force and know what
1716 # you are doing or used --exact or a merge patch while 1748 # you are doing or used --exact or a merge patch while
1717 # being updated to its first parent. 1749 # being updated to its first parent.
1718 m = None 1750 m = None
1719 else: 1751 else:
1720 m = scmutil.matchfiles(repo, files or []) 1752 m = scmutil.matchfiles(repo, files or [])
1721 editform = mergeeditform(repo[None], 'import.normal') 1753 editform = mergeeditform(repo[None], b'import.normal')
1722 if opts.get('exact'): 1754 if opts.get(b'exact'):
1723 editor = None 1755 editor = None
1724 else: 1756 else:
1725 editor = getcommiteditor( 1757 editor = getcommiteditor(
1726 editform=editform, **pycompat.strkwargs(opts) 1758 editform=editform, **pycompat.strkwargs(opts)
1727 ) 1759 )
1728 extra = {} 1760 extra = {}
1729 for idfunc in extrapreimport: 1761 for idfunc in extrapreimport:
1730 extrapreimportmap[idfunc](repo, patchdata, extra, opts) 1762 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1731 overrides = {} 1763 overrides = {}
1732 if partial: 1764 if partial:
1733 overrides[('ui', 'allowemptycommit')] = True 1765 overrides[(b'ui', b'allowemptycommit')] = True
1734 with repo.ui.configoverride(overrides, 'import'): 1766 with repo.ui.configoverride(overrides, b'import'):
1735 n = repo.commit( 1767 n = repo.commit(
1736 message, user, date, match=m, editor=editor, extra=extra 1768 message, user, date, match=m, editor=editor, extra=extra
1737 ) 1769 )
1738 for idfunc in extrapostimport: 1770 for idfunc in extrapostimport:
1739 extrapostimportmap[idfunc](repo[n]) 1771 extrapostimportmap[idfunc](repo[n])
1740 else: 1772 else:
1741 if opts.get('exact') or importbranch: 1773 if opts.get(b'exact') or importbranch:
1742 branch = branch or 'default' 1774 branch = branch or b'default'
1743 else: 1775 else:
1744 branch = p1.branch() 1776 branch = p1.branch()
1745 store = patch.filestore() 1777 store = patch.filestore()
1746 try: 1778 try:
1747 files = set() 1779 files = set()
1757 files, 1789 files,
1758 eolmode=None, 1790 eolmode=None,
1759 ) 1791 )
1760 except error.PatchError as e: 1792 except error.PatchError as e:
1761 raise error.Abort(stringutil.forcebytestr(e)) 1793 raise error.Abort(stringutil.forcebytestr(e))
1762 if opts.get('exact'): 1794 if opts.get(b'exact'):
1763 editor = None 1795 editor = None
1764 else: 1796 else:
1765 editor = getcommiteditor(editform='import.bypass') 1797 editor = getcommiteditor(editform=b'import.bypass')
1766 memctx = context.memctx( 1798 memctx = context.memctx(
1767 repo, 1799 repo,
1768 (p1.node(), p2.node()), 1800 (p1.node(), p2.node()),
1769 message, 1801 message,
1770 files=files, 1802 files=files,
1775 editor=editor, 1807 editor=editor,
1776 ) 1808 )
1777 n = memctx.commit() 1809 n = memctx.commit()
1778 finally: 1810 finally:
1779 store.close() 1811 store.close()
1780 if opts.get('exact') and nocommit: 1812 if opts.get(b'exact') and nocommit:
1781 # --exact with --no-commit is still useful in that it does merge 1813 # --exact with --no-commit is still useful in that it does merge
1782 # and branch bits 1814 # and branch bits
1783 ui.warn(_("warning: can't check exact import with --no-commit\n")) 1815 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1784 elif opts.get('exact') and (not n or hex(n) != nodeid): 1816 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1785 raise error.Abort(_('patch is damaged or loses information')) 1817 raise error.Abort(_(b'patch is damaged or loses information'))
1786 msg = _('applied to working directory') 1818 msg = _(b'applied to working directory')
1787 if n: 1819 if n:
1788 # i18n: refers to a short changeset id 1820 # i18n: refers to a short changeset id
1789 msg = _('created %s') % short(n) 1821 msg = _(b'created %s') % short(n)
1790 return msg, n, rejects 1822 return msg, n, rejects
1791 1823
1792 1824
1793 # facility to let extensions include additional data in an exported patch 1825 # facility to let extensions include additional data in an exported patch
1794 # list of identifiers to be executed in order 1826 # list of identifiers to be executed in order
1810 prev = parents[0] 1842 prev = parents[0]
1811 else: 1843 else:
1812 prev = nullid 1844 prev = nullid
1813 1845
1814 fm.context(ctx=ctx) 1846 fm.context(ctx=ctx)
1815 fm.plain('# HG changeset patch\n') 1847 fm.plain(b'# HG changeset patch\n')
1816 fm.write('user', '# User %s\n', ctx.user()) 1848 fm.write(b'user', b'# User %s\n', ctx.user())
1817 fm.plain('# Date %d %d\n' % ctx.date()) 1849 fm.plain(b'# Date %d %d\n' % ctx.date())
1818 fm.write('date', '# %s\n', fm.formatdate(ctx.date())) 1850 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1819 fm.condwrite( 1851 fm.condwrite(
1820 branch and branch != 'default', 'branch', '# Branch %s\n', branch 1852 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1821 ) 1853 )
1822 fm.write('node', '# Node ID %s\n', hex(node)) 1854 fm.write(b'node', b'# Node ID %s\n', hex(node))
1823 fm.plain('# Parent %s\n' % hex(prev)) 1855 fm.plain(b'# Parent %s\n' % hex(prev))
1824 if len(parents) > 1: 1856 if len(parents) > 1:
1825 fm.plain('# Parent %s\n' % hex(parents[1])) 1857 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1826 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name='node')) 1858 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1827 1859
1828 # TODO: redesign extraexportmap function to support formatter 1860 # TODO: redesign extraexportmap function to support formatter
1829 for headerid in extraexport: 1861 for headerid in extraexport:
1830 header = extraexportmap[headerid](seqno, ctx) 1862 header = extraexportmap[headerid](seqno, ctx)
1831 if header is not None: 1863 if header is not None:
1832 fm.plain('# %s\n' % header) 1864 fm.plain(b'# %s\n' % header)
1833 1865
1834 fm.write('desc', '%s\n', ctx.description().rstrip()) 1866 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1835 fm.plain('\n') 1867 fm.plain(b'\n')
1836 1868
1837 if fm.isplain(): 1869 if fm.isplain():
1838 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts) 1870 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1839 for chunk, label in chunkiter: 1871 for chunk, label in chunkiter:
1840 fm.plain(chunk, label=label) 1872 fm.plain(chunk, label=label)
1846 1878
1847 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match): 1879 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1848 """Export changesets to stdout or a single file""" 1880 """Export changesets to stdout or a single file"""
1849 for seqno, rev in enumerate(revs, 1): 1881 for seqno, rev in enumerate(revs, 1):
1850 ctx = repo[rev] 1882 ctx = repo[rev]
1851 if not dest.startswith('<'): 1883 if not dest.startswith(b'<'):
1852 repo.ui.note("%s\n" % dest) 1884 repo.ui.note(b"%s\n" % dest)
1853 fm.startitem() 1885 fm.startitem()
1854 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts) 1886 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1855 1887
1856 1888
1857 def _exportfntemplate( 1889 def _exportfntemplate(
1869 ) 1901 )
1870 filemap.setdefault(dest, []).append((seqno, rev)) 1902 filemap.setdefault(dest, []).append((seqno, rev))
1871 1903
1872 for dest in filemap: 1904 for dest in filemap:
1873 with formatter.maybereopen(basefm, dest) as fm: 1905 with formatter.maybereopen(basefm, dest) as fm:
1874 repo.ui.note("%s\n" % dest) 1906 repo.ui.note(b"%s\n" % dest)
1875 for seqno, rev in filemap[dest]: 1907 for seqno, rev in filemap[dest]:
1876 fm.startitem() 1908 fm.startitem()
1877 ctx = repo[rev] 1909 ctx = repo[rev]
1878 _exportsingle( 1910 _exportsingle(
1879 repo, ctx, fm, match, switch_parent, seqno, diffopts 1911 repo, ctx, fm, match, switch_parent, seqno, diffopts
1891 1923
1892 def export( 1924 def export(
1893 repo, 1925 repo,
1894 revs, 1926 revs,
1895 basefm, 1927 basefm,
1896 fntemplate='hg-%h.patch', 1928 fntemplate=b'hg-%h.patch',
1897 switch_parent=False, 1929 switch_parent=False,
1898 opts=None, 1930 opts=None,
1899 match=None, 1931 match=None,
1900 ): 1932 ):
1901 '''export changesets as hg patches 1933 '''export changesets as hg patches
1921 Otherwise: All revs will be written to basefm. 1953 Otherwise: All revs will be written to basefm.
1922 ''' 1954 '''
1923 _prefetchchangedfiles(repo, revs, match) 1955 _prefetchchangedfiles(repo, revs, match)
1924 1956
1925 if not fntemplate: 1957 if not fntemplate:
1926 _exportfile(repo, revs, basefm, '<unnamed>', switch_parent, opts, match) 1958 _exportfile(
1959 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
1960 )
1927 else: 1961 else:
1928 _exportfntemplate( 1962 _exportfntemplate(
1929 repo, revs, basefm, fntemplate, switch_parent, opts, match 1963 repo, revs, basefm, fntemplate, switch_parent, opts, match
1930 ) 1964 )
1931 1965
1932 1966
1933 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None): 1967 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1934 """Export changesets to the given file stream""" 1968 """Export changesets to the given file stream"""
1935 _prefetchchangedfiles(repo, revs, match) 1969 _prefetchchangedfiles(repo, revs, match)
1936 1970
1937 dest = getattr(fp, 'name', '<unnamed>') 1971 dest = getattr(fp, 'name', b'<unnamed>')
1938 with formatter.formatter(repo.ui, fp, 'export', {}) as fm: 1972 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
1939 _exportfile(repo, revs, fm, dest, switch_parent, opts, match) 1973 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1940 1974
1941 1975
1942 def showmarker(fm, marker, index=None): 1976 def showmarker(fm, marker, index=None):
1943 """utility function to display obsolescence marker in a readable way 1977 """utility function to display obsolescence marker in a readable way
1944 1978
1945 To be used by debug function.""" 1979 To be used by debug function."""
1946 if index is not None: 1980 if index is not None:
1947 fm.write('index', '%i ', index) 1981 fm.write(b'index', b'%i ', index)
1948 fm.write('prednode', '%s ', hex(marker.prednode())) 1982 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
1949 succs = marker.succnodes() 1983 succs = marker.succnodes()
1950 fm.condwrite( 1984 fm.condwrite(
1951 succs, 'succnodes', '%s ', fm.formatlist(map(hex, succs), name='node') 1985 succs,
1986 b'succnodes',
1987 b'%s ',
1988 fm.formatlist(map(hex, succs), name=b'node'),
1952 ) 1989 )
1953 fm.write('flag', '%X ', marker.flags()) 1990 fm.write(b'flag', b'%X ', marker.flags())
1954 parents = marker.parentnodes() 1991 parents = marker.parentnodes()
1955 if parents is not None: 1992 if parents is not None:
1956 fm.write( 1993 fm.write(
1957 'parentnodes', 1994 b'parentnodes',
1958 '{%s} ', 1995 b'{%s} ',
1959 fm.formatlist(map(hex, parents), name='node', sep=', '), 1996 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
1960 ) 1997 )
1961 fm.write('date', '(%s) ', fm.formatdate(marker.date())) 1998 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
1962 meta = marker.metadata().copy() 1999 meta = marker.metadata().copy()
1963 meta.pop('date', None) 2000 meta.pop(b'date', None)
1964 smeta = pycompat.rapply(pycompat.maybebytestr, meta) 2001 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
1965 fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', ')) 2002 fm.write(
1966 fm.plain('\n') 2003 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2004 )
2005 fm.plain(b'\n')
1967 2006
1968 2007
1969 def finddate(ui, repo, date): 2008 def finddate(ui, repo, date):
1970 """Find the tipmost changeset that matches the given date spec""" 2009 """Find the tipmost changeset that matches the given date spec"""
1971 2010
1976 def prep(ctx, fns): 2015 def prep(ctx, fns):
1977 d = ctx.date() 2016 d = ctx.date()
1978 if df(d[0]): 2017 if df(d[0]):
1979 results[ctx.rev()] = d 2018 results[ctx.rev()] = d
1980 2019
1981 for ctx in walkchangerevs(repo, m, {'rev': None}, prep): 2020 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
1982 rev = ctx.rev() 2021 rev = ctx.rev()
1983 if rev in results: 2022 if rev in results:
1984 ui.status( 2023 ui.status(
1985 _("found revision %s from %s\n") 2024 _(b"found revision %s from %s\n")
1986 % (rev, dateutil.datestr(results[rev])) 2025 % (rev, dateutil.datestr(results[rev]))
1987 ) 2026 )
1988 return '%d' % rev 2027 return b'%d' % rev
1989 2028
1990 raise error.Abort(_("revision matching date not found")) 2029 raise error.Abort(_(b"revision matching date not found"))
1991 2030
1992 2031
1993 def increasingwindows(windowsize=8, sizelimit=512): 2032 def increasingwindows(windowsize=8, sizelimit=512):
1994 while True: 2033 while True:
1995 yield windowsize 2034 yield windowsize
1998 2037
1999 2038
2000 def _walkrevs(repo, opts): 2039 def _walkrevs(repo, opts):
2001 # Default --rev value depends on --follow but --follow behavior 2040 # Default --rev value depends on --follow but --follow behavior
2002 # depends on revisions resolved from --rev... 2041 # depends on revisions resolved from --rev...
2003 follow = opts.get('follow') or opts.get('follow_first') 2042 follow = opts.get(b'follow') or opts.get(b'follow_first')
2004 if opts.get('rev'): 2043 if opts.get(b'rev'):
2005 revs = scmutil.revrange(repo, opts['rev']) 2044 revs = scmutil.revrange(repo, opts[b'rev'])
2006 elif follow and repo.dirstate.p1() == nullid: 2045 elif follow and repo.dirstate.p1() == nullid:
2007 revs = smartset.baseset() 2046 revs = smartset.baseset()
2008 elif follow: 2047 elif follow:
2009 revs = repo.revs('reverse(:.)') 2048 revs = repo.revs(b'reverse(:.)')
2010 else: 2049 else:
2011 revs = smartset.spanset(repo) 2050 revs = smartset.spanset(repo)
2012 revs.reverse() 2051 revs.reverse()
2013 return revs 2052 return revs
2014 2053
2058 ) 2097 )
2059 2098
2060 return reversed(revs) 2099 return reversed(revs)
2061 2100
2062 def iterfiles(): 2101 def iterfiles():
2063 pctx = repo['.'] 2102 pctx = repo[b'.']
2064 for filename in match.files(): 2103 for filename in match.files():
2065 if follow: 2104 if follow:
2066 if filename not in pctx: 2105 if filename not in pctx:
2067 raise error.Abort( 2106 raise error.Abort(
2068 _('cannot follow file not in parent ' 'revision: "%s"') 2107 _(
2108 b'cannot follow file not in parent '
2109 b'revision: "%s"'
2110 )
2069 % filename 2111 % filename
2070 ) 2112 )
2071 yield filename, pctx[filename].filenode() 2113 yield filename, pctx[filename].filenode()
2072 else: 2114 else:
2073 yield filename, None 2115 yield filename, None
2080 if node is None: 2122 if node is None:
2081 # A zero count may be a directory or deleted file, so 2123 # A zero count may be a directory or deleted file, so
2082 # try to find matching entries on the slow path. 2124 # try to find matching entries on the slow path.
2083 if follow: 2125 if follow:
2084 raise error.Abort( 2126 raise error.Abort(
2085 _('cannot follow nonexistent file: "%s"') % file_ 2127 _(b'cannot follow nonexistent file: "%s"') % file_
2086 ) 2128 )
2087 raise FileWalkError("Cannot walk via filelog") 2129 raise FileWalkError(b"Cannot walk via filelog")
2088 else: 2130 else:
2089 continue 2131 continue
2090 2132
2091 if node is None: 2133 if node is None:
2092 last = len(filelog) - 1 2134 last = len(filelog) - 1
2174 2216
2175 This function returns an iterator yielding contexts. Before 2217 This function returns an iterator yielding contexts. Before
2176 yielding each context, the iterator will first call the prepare 2218 yielding each context, the iterator will first call the prepare
2177 function on each context in the window in forward order.''' 2219 function on each context in the window in forward order.'''
2178 2220
2179 allfiles = opts.get('all_files') 2221 allfiles = opts.get(b'all_files')
2180 follow = opts.get('follow') or opts.get('follow_first') 2222 follow = opts.get(b'follow') or opts.get(b'follow_first')
2181 revs = _walkrevs(repo, opts) 2223 revs = _walkrevs(repo, opts)
2182 if not revs: 2224 if not revs:
2183 return [] 2225 return []
2184 wanted = set() 2226 wanted = set()
2185 slowpath = match.anypats() or (not match.always() and opts.get('removed')) 2227 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2186 fncache = {} 2228 fncache = {}
2187 change = repo.__getitem__ 2229 change = repo.__getitem__
2188 2230
2189 # First step is to fill wanted, the set of revisions that we want to yield. 2231 # First step is to fill wanted, the set of revisions that we want to yield.
2190 # When it does not induce extra cost, we also fill fncache for revisions in 2232 # When it does not induce extra cost, we also fill fncache for revisions in
2204 2246
2205 # We decided to fall back to the slowpath because at least one 2247 # We decided to fall back to the slowpath because at least one
2206 # of the paths was not a file. Check to see if at least one of them 2248 # of the paths was not a file. Check to see if at least one of them
2207 # existed in history, otherwise simply return 2249 # existed in history, otherwise simply return
2208 for path in match.files(): 2250 for path in match.files():
2209 if path == '.' or path in repo.store: 2251 if path == b'.' or path in repo.store:
2210 break 2252 break
2211 else: 2253 else:
2212 return [] 2254 return []
2213 2255
2214 if slowpath: 2256 if slowpath:
2215 # We have to read the changelog to match filenames against 2257 # We have to read the changelog to match filenames against
2216 # changed files 2258 # changed files
2217 2259
2218 if follow: 2260 if follow:
2219 raise error.Abort( 2261 raise error.Abort(
2220 _('can only follow copies/renames for explicit ' 'filenames') 2262 _(b'can only follow copies/renames for explicit ' b'filenames')
2221 ) 2263 )
2222 2264
2223 # The slow path checks files modified in every changeset. 2265 # The slow path checks files modified in every changeset.
2224 # This is really slow on large repos, so compute the set lazily. 2266 # This is really slow on large repos, so compute the set lazily.
2225 class lazywantedset(object): 2267 class lazywantedset(object):
2253 2295
2254 wanted = lazywantedset() 2296 wanted = lazywantedset()
2255 2297
2256 # it might be worthwhile to do this in the iterator if the rev range 2298 # it might be worthwhile to do this in the iterator if the rev range
2257 # is descending and the prune args are all within that range 2299 # is descending and the prune args are all within that range
2258 for rev in opts.get('prune', ()): 2300 for rev in opts.get(b'prune', ()):
2259 rev = repo[rev].rev() 2301 rev = repo[rev].rev()
2260 ff = _followfilter(repo) 2302 ff = _followfilter(repo)
2261 stop = min(revs[0], revs[-1]) 2303 stop = min(revs[0], revs[-1])
2262 for x in pycompat.xrange(rev, stop - 1, -1): 2304 for x in pycompat.xrange(rev, stop - 1, -1):
2263 if ff.match(x): 2305 if ff.match(x):
2265 2307
2266 # Now that wanted is correctly initialized, we can iterate over the 2308 # Now that wanted is correctly initialized, we can iterate over the
2267 # revision range, yielding only revisions in wanted. 2309 # revision range, yielding only revisions in wanted.
2268 def iterate(): 2310 def iterate():
2269 if follow and match.always(): 2311 if follow and match.always():
2270 ff = _followfilter(repo, onlyfirst=opts.get('follow_first')) 2312 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2271 2313
2272 def want(rev): 2314 def want(rev):
2273 return ff.match(rev) and rev in wanted 2315 return ff.match(rev) and rev in wanted
2274 2316
2275 else: 2317 else:
2343 if cca: 2385 if cca:
2344 cca(f) 2386 cca(f)
2345 names.append(f) 2387 names.append(f)
2346 if ui.verbose or not exact: 2388 if ui.verbose or not exact:
2347 ui.status( 2389 ui.status(
2348 _('adding %s\n') % uipathfn(f), label='ui.addremove.added' 2390 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2349 ) 2391 )
2350 2392
2351 for subpath in sorted(wctx.substate): 2393 for subpath in sorted(wctx.substate):
2352 sub = wctx.sub(subpath) 2394 sub = wctx.sub(subpath)
2353 try: 2395 try:
2362 bad.extend( 2404 bad.extend(
2363 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts) 2405 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2364 ) 2406 )
2365 except error.LookupError: 2407 except error.LookupError:
2366 ui.status( 2408 ui.status(
2367 _("skipping missing subrepository: %s\n") % uipathfn(subpath) 2409 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2368 ) 2410 )
2369 2411
2370 if not opts.get(r'dry_run'): 2412 if not opts.get(r'dry_run'):
2371 rejected = wctx.add(names, prefix) 2413 rejected = wctx.add(names, prefix)
2372 bad.extend(f for f in rejected if f in match.files()) 2414 bad.extend(f for f in rejected if f in match.files())
2373 return bad 2415 return bad
2374 2416
2375 2417
2376 def addwebdirpath(repo, serverpath, webconf): 2418 def addwebdirpath(repo, serverpath, webconf):
2377 webconf[serverpath] = repo.root 2419 webconf[serverpath] = repo.root
2378 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root)) 2420 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2379 2421
2380 for r in repo.revs('filelog("path:.hgsub")'): 2422 for r in repo.revs(b'filelog("path:.hgsub")'):
2381 ctx = repo[r] 2423 ctx = repo[r]
2382 for subpath in ctx.substate: 2424 for subpath in ctx.substate:
2383 ctx.sub(subpath).addwebdirpath(serverpath, webconf) 2425 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2384 2426
2385 2427
2386 def forget( 2428 def forget(
2387 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive 2429 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2388 ): 2430 ):
2389 if dryrun and interactive: 2431 if dryrun and interactive:
2390 raise error.Abort(_("cannot specify both --dry-run and --interactive")) 2432 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2391 bad = [] 2433 bad = []
2392 badfn = lambda x, y: bad.append(x) or match.bad(x, y) 2434 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2393 wctx = repo[None] 2435 wctx = repo[None]
2394 forgot = [] 2436 forgot = []
2395 2437
2409 subprefix, 2451 subprefix,
2410 subuipathfn, 2452 subuipathfn,
2411 dryrun=dryrun, 2453 dryrun=dryrun,
2412 interactive=interactive, 2454 interactive=interactive,
2413 ) 2455 )
2414 bad.extend([subpath + '/' + f for f in subbad]) 2456 bad.extend([subpath + b'/' + f for f in subbad])
2415 forgot.extend([subpath + '/' + f for f in subforgot]) 2457 forgot.extend([subpath + b'/' + f for f in subforgot])
2416 except error.LookupError: 2458 except error.LookupError:
2417 ui.status( 2459 ui.status(
2418 _("skipping missing subrepository: %s\n") % uipathfn(subpath) 2460 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2419 ) 2461 )
2420 2462
2421 if not explicitonly: 2463 if not explicitonly:
2422 for f in match.files(): 2464 for f in match.files():
2423 if f not in repo.dirstate and not repo.wvfs.isdir(f): 2465 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2428 # that subrepo files aren't normalized, and this op is 2470 # that subrepo files aren't normalized, and this op is
2429 # purely from data cached by the status walk above. 2471 # purely from data cached by the status walk above.
2430 if repo.dirstate.normalize(f) in repo.dirstate: 2472 if repo.dirstate.normalize(f) in repo.dirstate:
2431 continue 2473 continue
2432 ui.warn( 2474 ui.warn(
2433 _('not removing %s: ' 'file is already untracked\n') 2475 _(
2476 b'not removing %s: '
2477 b'file is already untracked\n'
2478 )
2434 % uipathfn(f) 2479 % uipathfn(f)
2435 ) 2480 )
2436 bad.append(f) 2481 bad.append(f)
2437 2482
2438 if interactive: 2483 if interactive:
2439 responses = _( 2484 responses = _(
2440 '[Ynsa?]' 2485 b'[Ynsa?]'
2441 '$$ &Yes, forget this file' 2486 b'$$ &Yes, forget this file'
2442 '$$ &No, skip this file' 2487 b'$$ &No, skip this file'
2443 '$$ &Skip remaining files' 2488 b'$$ &Skip remaining files'
2444 '$$ Include &all remaining files' 2489 b'$$ Include &all remaining files'
2445 '$$ &? (display help)' 2490 b'$$ &? (display help)'
2446 ) 2491 )
2447 for filename in forget[:]: 2492 for filename in forget[:]:
2448 r = ui.promptchoice( 2493 r = ui.promptchoice(
2449 _('forget %s %s') % (uipathfn(filename), responses) 2494 _(b'forget %s %s') % (uipathfn(filename), responses)
2450 ) 2495 )
2451 if r == 4: # ? 2496 if r == 4: # ?
2452 while r == 4: 2497 while r == 4:
2453 for c, t in ui.extractchoices(responses)[1]: 2498 for c, t in ui.extractchoices(responses)[1]:
2454 ui.write('%s - %s\n' % (c, encoding.lower(t))) 2499 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2455 r = ui.promptchoice( 2500 r = ui.promptchoice(
2456 _('forget %s %s') % (uipathfn(filename), responses) 2501 _(b'forget %s %s') % (uipathfn(filename), responses)
2457 ) 2502 )
2458 if r == 0: # yes 2503 if r == 0: # yes
2459 continue 2504 continue
2460 elif r == 1: # no 2505 elif r == 1: # no
2461 forget.remove(filename) 2506 forget.remove(filename)
2467 break 2512 break
2468 2513
2469 for f in forget: 2514 for f in forget:
2470 if ui.verbose or not match.exact(f) or interactive: 2515 if ui.verbose or not match.exact(f) or interactive:
2471 ui.status( 2516 ui.status(
2472 _('removing %s\n') % uipathfn(f), label='ui.addremove.removed' 2517 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2473 ) 2518 )
2474 2519
2475 if not dryrun: 2520 if not dryrun:
2476 rejected = wctx.forget(forget, prefix) 2521 rejected = wctx.forget(forget, prefix)
2477 bad.extend(f for f in rejected if f in match.files()) 2522 bad.extend(f for f in rejected if f in match.files())
2480 2525
2481 2526
2482 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos): 2527 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2483 ret = 1 2528 ret = 1
2484 2529
2485 needsfctx = ui.verbose or {'size', 'flags'} & fm.datahint() 2530 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2486 for f in ctx.matches(m): 2531 for f in ctx.matches(m):
2487 fm.startitem() 2532 fm.startitem()
2488 fm.context(ctx=ctx) 2533 fm.context(ctx=ctx)
2489 if needsfctx: 2534 if needsfctx:
2490 fc = ctx[f] 2535 fc = ctx[f]
2491 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags()) 2536 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2492 fm.data(path=f) 2537 fm.data(path=f)
2493 fm.plain(fmt % uipathfn(f)) 2538 fm.plain(fmt % uipathfn(f))
2494 ret = 0 2539 ret = 0
2495 2540
2496 for subpath in sorted(ctx.substate): 2541 for subpath in sorted(ctx.substate):
2505 == 0 2550 == 0
2506 ): 2551 ):
2507 ret = 0 2552 ret = 0
2508 except error.LookupError: 2553 except error.LookupError:
2509 ui.status( 2554 ui.status(
2510 _("skipping missing subrepository: %s\n") 2555 _(b"skipping missing subrepository: %s\n")
2511 % uipathfn(subpath) 2556 % uipathfn(subpath)
2512 ) 2557 )
2513 2558
2514 return ret 2559 return ret
2515 2560
2529 else: 2574 else:
2530 warn = False 2575 warn = False
2531 2576
2532 subs = sorted(wctx.substate) 2577 subs = sorted(wctx.substate)
2533 progress = ui.makeprogress( 2578 progress = ui.makeprogress(
2534 _('searching'), total=len(subs), unit=_('subrepos') 2579 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2535 ) 2580 )
2536 for subpath in subs: 2581 for subpath in subs:
2537 submatch = matchmod.subdirmatcher(subpath, m) 2582 submatch = matchmod.subdirmatcher(subpath, m)
2538 subprefix = repo.wvfs.reljoin(prefix, subpath) 2583 subprefix = repo.wvfs.reljoin(prefix, subpath)
2539 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn) 2584 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2552 warnings, 2597 warnings,
2553 ): 2598 ):
2554 ret = 1 2599 ret = 1
2555 except error.LookupError: 2600 except error.LookupError:
2556 warnings.append( 2601 warnings.append(
2557 _("skipping missing subrepository: %s\n") 2602 _(b"skipping missing subrepository: %s\n")
2558 % uipathfn(subpath) 2603 % uipathfn(subpath)
2559 ) 2604 )
2560 progress.complete() 2605 progress.complete()
2561 2606
2562 # warn about failure to delete explicit files/dirs 2607 # warn about failure to delete explicit files/dirs
2563 deleteddirs = util.dirs(deleted) 2608 deleteddirs = util.dirs(deleted)
2564 files = m.files() 2609 files = m.files()
2565 progress = ui.makeprogress(_('deleting'), total=len(files), unit=_('files')) 2610 progress = ui.makeprogress(
2611 _(b'deleting'), total=len(files), unit=_(b'files')
2612 )
2566 for f in files: 2613 for f in files:
2567 2614
2568 def insubrepo(): 2615 def insubrepo():
2569 for subpath in wctx.substate: 2616 for subpath in wctx.substate:
2570 if f.startswith(subpath + '/'): 2617 if f.startswith(subpath + b'/'):
2571 return True 2618 return True
2572 return False 2619 return False
2573 2620
2574 progress.increment() 2621 progress.increment()
2575 isdir = f in deleteddirs or wctx.hasdir(f) 2622 isdir = f in deleteddirs or wctx.hasdir(f)
2576 if f in repo.dirstate or isdir or f == '.' or insubrepo() or f in subs: 2623 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2577 continue 2624 continue
2578 2625
2579 if repo.wvfs.exists(f): 2626 if repo.wvfs.exists(f):
2580 if repo.wvfs.isdir(f): 2627 if repo.wvfs.isdir(f):
2581 warnings.append( 2628 warnings.append(
2582 _('not removing %s: no tracked files\n') % uipathfn(f) 2629 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2583 ) 2630 )
2584 else: 2631 else:
2585 warnings.append( 2632 warnings.append(
2586 _('not removing %s: file is untracked\n') % uipathfn(f) 2633 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2587 ) 2634 )
2588 # missing files will generate a warning elsewhere 2635 # missing files will generate a warning elsewhere
2589 ret = 1 2636 ret = 1
2590 progress.complete() 2637 progress.complete()
2591 2638
2593 list = modified + deleted + clean + added 2640 list = modified + deleted + clean + added
2594 elif after: 2641 elif after:
2595 list = deleted 2642 list = deleted
2596 remaining = modified + added + clean 2643 remaining = modified + added + clean
2597 progress = ui.makeprogress( 2644 progress = ui.makeprogress(
2598 _('skipping'), total=len(remaining), unit=_('files') 2645 _(b'skipping'), total=len(remaining), unit=_(b'files')
2599 ) 2646 )
2600 for f in remaining: 2647 for f in remaining:
2601 progress.increment() 2648 progress.increment()
2602 if ui.verbose or (f in files): 2649 if ui.verbose or (f in files):
2603 warnings.append( 2650 warnings.append(
2604 _('not removing %s: file still exists\n') % uipathfn(f) 2651 _(b'not removing %s: file still exists\n') % uipathfn(f)
2605 ) 2652 )
2606 ret = 1 2653 ret = 1
2607 progress.complete() 2654 progress.complete()
2608 else: 2655 else:
2609 list = deleted + clean 2656 list = deleted + clean
2610 progress = ui.makeprogress( 2657 progress = ui.makeprogress(
2611 _('skipping'), total=(len(modified) + len(added)), unit=_('files') 2658 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2612 ) 2659 )
2613 for f in modified: 2660 for f in modified:
2614 progress.increment() 2661 progress.increment()
2615 warnings.append( 2662 warnings.append(
2616 _( 2663 _(
2617 'not removing %s: file is modified (use -f' 2664 b'not removing %s: file is modified (use -f'
2618 ' to force removal)\n' 2665 b' to force removal)\n'
2619 ) 2666 )
2620 % uipathfn(f) 2667 % uipathfn(f)
2621 ) 2668 )
2622 ret = 1 2669 ret = 1
2623 for f in added: 2670 for f in added:
2624 progress.increment() 2671 progress.increment()
2625 warnings.append( 2672 warnings.append(
2626 _( 2673 _(
2627 "not removing %s: file has been marked for add" 2674 b"not removing %s: file has been marked for add"
2628 " (use 'hg forget' to undo add)\n" 2675 b" (use 'hg forget' to undo add)\n"
2629 ) 2676 )
2630 % uipathfn(f) 2677 % uipathfn(f)
2631 ) 2678 )
2632 ret = 1 2679 ret = 1
2633 progress.complete() 2680 progress.complete()
2634 2681
2635 list = sorted(list) 2682 list = sorted(list)
2636 progress = ui.makeprogress(_('deleting'), total=len(list), unit=_('files')) 2683 progress = ui.makeprogress(
2684 _(b'deleting'), total=len(list), unit=_(b'files')
2685 )
2637 for f in list: 2686 for f in list:
2638 if ui.verbose or not m.exact(f): 2687 if ui.verbose or not m.exact(f):
2639 progress.increment() 2688 progress.increment()
2640 ui.status( 2689 ui.status(
2641 _('removing %s\n') % uipathfn(f), label='ui.addremove.removed' 2690 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2642 ) 2691 )
2643 progress.complete() 2692 progress.complete()
2644 2693
2645 if not dryrun: 2694 if not dryrun:
2646 with repo.wlock(): 2695 with repo.wlock():
2647 if not after: 2696 if not after:
2648 for f in list: 2697 for f in list:
2649 if f in added: 2698 if f in added:
2650 continue # we never unlink added files on remove 2699 continue # we never unlink added files on remove
2651 rmdir = repo.ui.configbool( 2700 rmdir = repo.ui.configbool(
2652 'experimental', 'removeemptydirs' 2701 b'experimental', b'removeemptydirs'
2653 ) 2702 )
2654 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir) 2703 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2655 repo[None].forget(list) 2704 repo[None].forget(list)
2656 2705
2657 if warn: 2706 if warn:
2660 2709
2661 return ret 2710 return ret
2662 2711
2663 2712
2664 def _catfmtneedsdata(fm): 2713 def _catfmtneedsdata(fm):
2665 return not fm.datahint() or 'data' in fm.datahint() 2714 return not fm.datahint() or b'data' in fm.datahint()
2666 2715
2667 2716
2668 def _updatecatformatter(fm, ctx, matcher, path, decode): 2717 def _updatecatformatter(fm, ctx, matcher, path, decode):
2669 """Hook for adding data to the formatter used by ``hg cat``. 2718 """Hook for adding data to the formatter used by ``hg cat``.
2670 2719
2678 data = ctx[path].data() 2727 data = ctx[path].data()
2679 if decode: 2728 if decode:
2680 data = ctx.repo().wwritedata(path, data) 2729 data = ctx.repo().wwritedata(path, data)
2681 fm.startitem() 2730 fm.startitem()
2682 fm.context(ctx=ctx) 2731 fm.context(ctx=ctx)
2683 fm.write('data', '%s', data) 2732 fm.write(b'data', b'%s', data)
2684 fm.data(path=path) 2733 fm.data(path=path)
2685 2734
2686 2735
2687 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts): 2736 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2688 err = 1 2737 err = 1
2698 try: 2747 try:
2699 os.makedirs(os.path.dirname(filename)) 2748 os.makedirs(os.path.dirname(filename))
2700 except OSError: 2749 except OSError:
2701 pass 2750 pass
2702 with formatter.maybereopen(basefm, filename) as fm: 2751 with formatter.maybereopen(basefm, filename) as fm:
2703 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode')) 2752 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2704 2753
2705 # Automation often uses hg cat on single files, so special case it 2754 # Automation often uses hg cat on single files, so special case it
2706 # for performance to avoid the cost of parsing the manifest. 2755 # for performance to avoid the cost of parsing the manifest.
2707 if len(matcher.files()) == 1 and not matcher.anypats(): 2756 if len(matcher.files()) == 1 and not matcher.anypats():
2708 file = matcher.files()[0] 2757 file = matcher.files()[0]
2738 **pycompat.strkwargs(opts) 2787 **pycompat.strkwargs(opts)
2739 ): 2788 ):
2740 err = 0 2789 err = 0
2741 except error.RepoLookupError: 2790 except error.RepoLookupError:
2742 ui.status( 2791 ui.status(
2743 _("skipping missing subrepository: %s\n") % uipathfn(subpath) 2792 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2744 ) 2793 )
2745 2794
2746 return err 2795 return err
2747 2796
2748 2797
2749 def commit(ui, repo, commitfunc, pats, opts): 2798 def commit(ui, repo, commitfunc, pats, opts):
2750 '''commit the specified files or all outstanding changes''' 2799 '''commit the specified files or all outstanding changes'''
2751 date = opts.get('date') 2800 date = opts.get(b'date')
2752 if date: 2801 if date:
2753 opts['date'] = dateutil.parsedate(date) 2802 opts[b'date'] = dateutil.parsedate(date)
2754 message = logmessage(ui, opts) 2803 message = logmessage(ui, opts)
2755 matcher = scmutil.match(repo[None], pats, opts) 2804 matcher = scmutil.match(repo[None], pats, opts)
2756 2805
2757 dsguard = None 2806 dsguard = None
2758 # extract addremove carefully -- this function can be called from a command 2807 # extract addremove carefully -- this function can be called from a command
2759 # that doesn't support addremove 2808 # that doesn't support addremove
2760 if opts.get('addremove'): 2809 if opts.get(b'addremove'):
2761 dsguard = dirstateguard.dirstateguard(repo, 'commit') 2810 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2762 with dsguard or util.nullcontextmanager(): 2811 with dsguard or util.nullcontextmanager():
2763 if dsguard: 2812 if dsguard:
2764 relative = scmutil.anypats(pats, opts) 2813 relative = scmutil.anypats(pats, opts)
2765 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative) 2814 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2766 if scmutil.addremove(repo, matcher, "", uipathfn, opts) != 0: 2815 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2767 raise error.Abort( 2816 raise error.Abort(
2768 _("failed to mark all new/missing files as added/removed") 2817 _(b"failed to mark all new/missing files as added/removed")
2769 ) 2818 )
2770 2819
2771 return commitfunc(ui, repo, message, matcher, opts) 2820 return commitfunc(ui, repo, message, matcher, opts)
2772 2821
2773 2822
2790 # amend will reuse the existing user if not specified, but the obsolete 2839 # amend will reuse the existing user if not specified, but the obsolete
2791 # marker creation requires that the current user's name is specified. 2840 # marker creation requires that the current user's name is specified.
2792 if obsolete.isenabled(repo, obsolete.createmarkersopt): 2841 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2793 ui.username() # raise exception if username not set 2842 ui.username() # raise exception if username not set
2794 2843
2795 ui.note(_('amending changeset %s\n') % old) 2844 ui.note(_(b'amending changeset %s\n') % old)
2796 base = old.p1() 2845 base = old.p1()
2797 2846
2798 with repo.wlock(), repo.lock(), repo.transaction('amend'): 2847 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2799 # Participating changesets: 2848 # Participating changesets:
2800 # 2849 #
2801 # wctx o - workingctx that contains changes from working copy 2850 # wctx o - workingctx that contains changes from working copy
2802 # | to go into amending commit 2851 # | to go into amending commit
2803 # | 2852 # |
2817 2866
2818 # date-only change should be ignored? 2867 # date-only change should be ignored?
2819 datemaydiffer = resolvecommitoptions(ui, opts) 2868 datemaydiffer = resolvecommitoptions(ui, opts)
2820 2869
2821 date = old.date() 2870 date = old.date()
2822 if opts.get('date'): 2871 if opts.get(b'date'):
2823 date = dateutil.parsedate(opts.get('date')) 2872 date = dateutil.parsedate(opts.get(b'date'))
2824 user = opts.get('user') or old.user() 2873 user = opts.get(b'user') or old.user()
2825 2874
2826 if len(old.parents()) > 1: 2875 if len(old.parents()) > 1:
2827 # ctx.files() isn't reliable for merges, so fall back to the 2876 # ctx.files() isn't reliable for merges, so fall back to the
2828 # slower repo.status() method 2877 # slower repo.status() method
2829 files = {fn for st in base.status(old)[:3] for fn in st} 2878 files = {fn for st in base.status(old)[:3] for fn in st}
2833 # add/remove the files to the working copy if the "addremove" option 2882 # add/remove the files to the working copy if the "addremove" option
2834 # was specified. 2883 # was specified.
2835 matcher = scmutil.match(wctx, pats, opts) 2884 matcher = scmutil.match(wctx, pats, opts)
2836 relative = scmutil.anypats(pats, opts) 2885 relative = scmutil.anypats(pats, opts)
2837 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative) 2886 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2838 if opts.get('addremove') and scmutil.addremove( 2887 if opts.get(b'addremove') and scmutil.addremove(
2839 repo, matcher, "", uipathfn, opts 2888 repo, matcher, b"", uipathfn, opts
2840 ): 2889 ):
2841 raise error.Abort( 2890 raise error.Abort(
2842 _("failed to mark all new/missing files as added/removed") 2891 _(b"failed to mark all new/missing files as added/removed")
2843 ) 2892 )
2844 2893
2845 # Check subrepos. This depends on in-place wctx._status update in 2894 # Check subrepos. This depends on in-place wctx._status update in
2846 # subrepo.precommit(). To minimize the risk of this hack, we do 2895 # subrepo.precommit(). To minimize the risk of this hack, we do
2847 # nothing if .hgsub does not exist. 2896 # nothing if .hgsub does not exist.
2848 if '.hgsub' in wctx or '.hgsub' in old: 2897 if b'.hgsub' in wctx or b'.hgsub' in old:
2849 subs, commitsubs, newsubstate = subrepoutil.precommit( 2898 subs, commitsubs, newsubstate = subrepoutil.precommit(
2850 ui, wctx, wctx._status, matcher 2899 ui, wctx, wctx._status, matcher
2851 ) 2900 )
2852 # amend should abort if commitsubrepos is enabled 2901 # amend should abort if commitsubrepos is enabled
2853 assert not commitsubs 2902 assert not commitsubs
2898 mctx = context.memfilectx( 2947 mctx = context.memfilectx(
2899 repo, 2948 repo,
2900 ctx_, 2949 ctx_,
2901 fctx.path(), 2950 fctx.path(),
2902 fctx.data(), 2951 fctx.data(),
2903 islink='l' in flags, 2952 islink=b'l' in flags,
2904 isexec='x' in flags, 2953 isexec=b'x' in flags,
2905 copysource=copied.get(path), 2954 copysource=copied.get(path),
2906 ) 2955 )
2907 return mctx 2956 return mctx
2908 except KeyError: 2957 except KeyError:
2909 return None 2958 return None
2910 2959
2911 else: 2960 else:
2912 ui.note(_('copying changeset %s to %s\n') % (old, base)) 2961 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2913 2962
2914 # Use version of files as in the old cset 2963 # Use version of files as in the old cset
2915 def filectxfn(repo, ctx_, path): 2964 def filectxfn(repo, ctx_, path):
2916 try: 2965 try:
2917 return old.filectx(path) 2966 return old.filectx(path)
2920 2969
2921 # See if we got a message from -m or -l, if not, open the editor with 2970 # See if we got a message from -m or -l, if not, open the editor with
2922 # the message of the changeset to amend. 2971 # the message of the changeset to amend.
2923 message = logmessage(ui, opts) 2972 message = logmessage(ui, opts)
2924 2973
2925 editform = mergeeditform(old, 'commit.amend') 2974 editform = mergeeditform(old, b'commit.amend')
2926 2975
2927 if not message: 2976 if not message:
2928 message = old.description() 2977 message = old.description()
2929 # Default if message isn't provided and --edit is not passed is to 2978 # Default if message isn't provided and --edit is not passed is to
2930 # invoke editor, but allow --no-edit. If somehow we don't have any 2979 # invoke editor, but allow --no-edit. If somehow we don't have any
2931 # description, let's always start the editor. 2980 # description, let's always start the editor.
2932 doedit = not message or opts.get('edit') in [True, None] 2981 doedit = not message or opts.get(b'edit') in [True, None]
2933 else: 2982 else:
2934 # Default if message is provided is to not invoke editor, but allow 2983 # Default if message is provided is to not invoke editor, but allow
2935 # --edit. 2984 # --edit.
2936 doedit = opts.get('edit') is True 2985 doedit = opts.get(b'edit') is True
2937 editor = getcommiteditor(edit=doedit, editform=editform) 2986 editor = getcommiteditor(edit=doedit, editform=editform)
2938 2987
2939 pureextra = extra.copy() 2988 pureextra = extra.copy()
2940 extra['amend_source'] = old.hex() 2989 extra[b'amend_source'] = old.hex()
2941 2990
2942 new = context.memctx( 2991 new = context.memctx(
2943 repo, 2992 repo,
2944 parents=[base.node(), old.p2().node()], 2993 parents=[base.node(), old.p2().node()],
2945 text=message, 2994 text=message,
2964 # 3013 #
2965 # This not what we expect from amend. 3014 # This not what we expect from amend.
2966 return old.node() 3015 return old.node()
2967 3016
2968 commitphase = None 3017 commitphase = None
2969 if opts.get('secret'): 3018 if opts.get(b'secret'):
2970 commitphase = phases.secret 3019 commitphase = phases.secret
2971 newid = repo.commitctx(new) 3020 newid = repo.commitctx(new)
2972 3021
2973 # Reroute the working copy parent to the new changeset 3022 # Reroute the working copy parent to the new changeset
2974 repo.setparents(newid, nullid) 3023 repo.setparents(newid, nullid)
2975 mapping = {old.node(): (newid,)} 3024 mapping = {old.node(): (newid,)}
2976 obsmetadata = None 3025 obsmetadata = None
2977 if opts.get('note'): 3026 if opts.get(b'note'):
2978 obsmetadata = {'note': encoding.fromlocal(opts['note'])} 3027 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
2979 backup = ui.configbool('rewrite', 'backup-bundle') 3028 backup = ui.configbool(b'rewrite', b'backup-bundle')
2980 scmutil.cleanupnodes( 3029 scmutil.cleanupnodes(
2981 repo, 3030 repo,
2982 mapping, 3031 mapping,
2983 'amend', 3032 b'amend',
2984 metadata=obsmetadata, 3033 metadata=obsmetadata,
2985 fixphase=True, 3034 fixphase=True,
2986 targetphase=commitphase, 3035 targetphase=commitphase,
2987 backup=backup, 3036 backup=backup,
2988 ) 3037 )
3007 dirstate.drop(f) 3056 dirstate.drop(f)
3008 3057
3009 return newid 3058 return newid
3010 3059
3011 3060
3012 def commiteditor(repo, ctx, subs, editform=''): 3061 def commiteditor(repo, ctx, subs, editform=b''):
3013 if ctx.description(): 3062 if ctx.description():
3014 return ctx.description() 3063 return ctx.description()
3015 return commitforceeditor( 3064 return commitforceeditor(
3016 repo, ctx, subs, editform=editform, unchangedmessagedetection=True 3065 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3017 ) 3066 )
3021 repo, 3070 repo,
3022 ctx, 3071 ctx,
3023 subs, 3072 subs,
3024 finishdesc=None, 3073 finishdesc=None,
3025 extramsg=None, 3074 extramsg=None,
3026 editform='', 3075 editform=b'',
3027 unchangedmessagedetection=False, 3076 unchangedmessagedetection=False,
3028 ): 3077 ):
3029 if not extramsg: 3078 if not extramsg:
3030 extramsg = _("Leave message empty to abort commit.") 3079 extramsg = _(b"Leave message empty to abort commit.")
3031 3080
3032 forms = [e for e in editform.split('.') if e] 3081 forms = [e for e in editform.split(b'.') if e]
3033 forms.insert(0, 'changeset') 3082 forms.insert(0, b'changeset')
3034 templatetext = None 3083 templatetext = None
3035 while forms: 3084 while forms:
3036 ref = '.'.join(forms) 3085 ref = b'.'.join(forms)
3037 if repo.ui.config('committemplate', ref): 3086 if repo.ui.config(b'committemplate', ref):
3038 templatetext = committext = buildcommittemplate( 3087 templatetext = committext = buildcommittemplate(
3039 repo, ctx, subs, extramsg, ref 3088 repo, ctx, subs, extramsg, ref
3040 ) 3089 )
3041 break 3090 break
3042 forms.pop() 3091 forms.pop()
3057 ctx.user(), 3106 ctx.user(),
3058 ctx.extra(), 3107 ctx.extra(),
3059 editform=editform, 3108 editform=editform,
3060 pending=pending, 3109 pending=pending,
3061 repopath=repo.path, 3110 repopath=repo.path,
3062 action='commit', 3111 action=b'commit',
3063 ) 3112 )
3064 text = editortext 3113 text = editortext
3065 3114
3066 # strip away anything below this special string (used for editors that want 3115 # strip away anything below this special string (used for editors that want
3067 # to display the diff) 3116 # to display the diff)
3068 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE) 3117 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3069 if stripbelow: 3118 if stripbelow:
3070 text = text[: stripbelow.start()] 3119 text = text[: stripbelow.start()]
3071 3120
3072 text = re.sub("(?m)^HG:.*(\n|$)", "", text) 3121 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3073 os.chdir(olddir) 3122 os.chdir(olddir)
3074 3123
3075 if finishdesc: 3124 if finishdesc:
3076 text = finishdesc(text) 3125 text = finishdesc(text)
3077 if not text.strip(): 3126 if not text.strip():
3078 raise error.Abort(_("empty commit message")) 3127 raise error.Abort(_(b"empty commit message"))
3079 if unchangedmessagedetection and editortext == templatetext: 3128 if unchangedmessagedetection and editortext == templatetext:
3080 raise error.Abort(_("commit message unchanged")) 3129 raise error.Abort(_(b"commit message unchanged"))
3081 3130
3082 return text 3131 return text
3083 3132
3084 3133
3085 def buildcommittemplate(repo, ctx, subs, extramsg, ref): 3134 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3086 ui = repo.ui 3135 ui = repo.ui
3087 spec = formatter.templatespec(ref, None, None) 3136 spec = formatter.templatespec(ref, None, None)
3088 t = logcmdutil.changesettemplater(ui, repo, spec) 3137 t = logcmdutil.changesettemplater(ui, repo, spec)
3089 t.t.cache.update( 3138 t.t.cache.update(
3090 (k, templater.unquotestring(v)) 3139 (k, templater.unquotestring(v))
3091 for k, v in repo.ui.configitems('committemplate') 3140 for k, v in repo.ui.configitems(b'committemplate')
3092 ) 3141 )
3093 3142
3094 if not extramsg: 3143 if not extramsg:
3095 extramsg = '' # ensure that extramsg is string 3144 extramsg = b'' # ensure that extramsg is string
3096 3145
3097 ui.pushbuffer() 3146 ui.pushbuffer()
3098 t.show(ctx, extramsg=extramsg) 3147 t.show(ctx, extramsg=extramsg)
3099 return ui.popbuffer() 3148 return ui.popbuffer()
3100 3149
3101 3150
3102 def hgprefix(msg): 3151 def hgprefix(msg):
3103 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a]) 3152 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3104 3153
3105 3154
3106 def buildcommittext(repo, ctx, subs, extramsg): 3155 def buildcommittext(repo, ctx, subs, extramsg):
3107 edittext = [] 3156 edittext = []
3108 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed() 3157 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3109 if ctx.description(): 3158 if ctx.description():
3110 edittext.append(ctx.description()) 3159 edittext.append(ctx.description())
3111 edittext.append("") 3160 edittext.append(b"")
3112 edittext.append("") # Empty line between message and comments. 3161 edittext.append(b"") # Empty line between message and comments.
3113 edittext.append( 3162 edittext.append(
3114 hgprefix( 3163 hgprefix(
3115 _( 3164 _(
3116 "Enter commit message." 3165 b"Enter commit message."
3117 " Lines beginning with 'HG:' are removed." 3166 b" Lines beginning with 'HG:' are removed."
3118 ) 3167 )
3119 ) 3168 )
3120 ) 3169 )
3121 edittext.append(hgprefix(extramsg)) 3170 edittext.append(hgprefix(extramsg))
3122 edittext.append("HG: --") 3171 edittext.append(b"HG: --")
3123 edittext.append(hgprefix(_("user: %s") % ctx.user())) 3172 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3124 if ctx.p2(): 3173 if ctx.p2():
3125 edittext.append(hgprefix(_("branch merge"))) 3174 edittext.append(hgprefix(_(b"branch merge")))
3126 if ctx.branch(): 3175 if ctx.branch():
3127 edittext.append(hgprefix(_("branch '%s'") % ctx.branch())) 3176 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3128 if bookmarks.isactivewdirparent(repo): 3177 if bookmarks.isactivewdirparent(repo):
3129 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark)) 3178 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3130 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs]) 3179 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3131 edittext.extend([hgprefix(_("added %s") % f) for f in added]) 3180 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3132 edittext.extend([hgprefix(_("changed %s") % f) for f in modified]) 3181 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3133 edittext.extend([hgprefix(_("removed %s") % f) for f in removed]) 3182 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3134 if not added and not modified and not removed: 3183 if not added and not modified and not removed:
3135 edittext.append(hgprefix(_("no files changed"))) 3184 edittext.append(hgprefix(_(b"no files changed")))
3136 edittext.append("") 3185 edittext.append(b"")
3137 3186
3138 return "\n".join(edittext) 3187 return b"\n".join(edittext)
3139 3188
3140 3189
3141 def commitstatus(repo, node, branch, bheads=None, opts=None): 3190 def commitstatus(repo, node, branch, bheads=None, opts=None):
3142 if opts is None: 3191 if opts is None:
3143 opts = {} 3192 opts = {}
3144 ctx = repo[node] 3193 ctx = repo[node]
3145 parents = ctx.parents() 3194 parents = ctx.parents()
3146 3195
3147 if ( 3196 if (
3148 not opts.get('amend') 3197 not opts.get(b'amend')
3149 and bheads 3198 and bheads
3150 and node not in bheads 3199 and node not in bheads
3151 and not [ 3200 and not [
3152 x for x in parents if x.node() in bheads and x.branch() == branch 3201 x for x in parents if x.node() in bheads and x.branch() == branch
3153 ] 3202 ]
3154 ): 3203 ):
3155 repo.ui.status(_('created new head\n')) 3204 repo.ui.status(_(b'created new head\n'))
3156 # The message is not printed for initial roots. For the other 3205 # The message is not printed for initial roots. For the other
3157 # changesets, it is printed in the following situations: 3206 # changesets, it is printed in the following situations:
3158 # 3207 #
3159 # Par column: for the 2 parents with ... 3208 # Par column: for the 2 parents with ...
3160 # N: null or no parent 3209 # N: null or no parent
3180 # C C y additional head from merge 3229 # C C y additional head from merge
3181 # C H n merge with a head 3230 # C H n merge with a head
3182 # 3231 #
3183 # H H n head merge: head count decreases 3232 # H H n head merge: head count decreases
3184 3233
3185 if not opts.get('close_branch'): 3234 if not opts.get(b'close_branch'):
3186 for r in parents: 3235 for r in parents:
3187 if r.closesbranch() and r.branch() == branch: 3236 if r.closesbranch() and r.branch() == branch:
3188 repo.ui.status(_('reopening closed branch head %d\n') % r.rev()) 3237 repo.ui.status(
3238 _(b'reopening closed branch head %d\n') % r.rev()
3239 )
3189 3240
3190 if repo.ui.debugflag: 3241 if repo.ui.debugflag:
3191 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())) 3242 repo.ui.write(
3243 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3244 )
3192 elif repo.ui.verbose: 3245 elif repo.ui.verbose:
3193 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx)) 3246 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3194 3247
3195 3248
3196 def postcommitstatus(repo, pats, opts): 3249 def postcommitstatus(repo, pats, opts):
3197 return repo.status(match=scmutil.match(repo[None], pats, opts)) 3250 return repo.status(match=scmutil.match(repo[None], pats, opts))
3198 3251
3220 3273
3221 with repo.wlock(): 3274 with repo.wlock():
3222 ## filling of the `names` mapping 3275 ## filling of the `names` mapping
3223 # walk dirstate to fill `names` 3276 # walk dirstate to fill `names`
3224 3277
3225 interactive = opts.get('interactive', False) 3278 interactive = opts.get(b'interactive', False)
3226 wctx = repo[None] 3279 wctx = repo[None]
3227 m = scmutil.match(wctx, pats, opts) 3280 m = scmutil.match(wctx, pats, opts)
3228 3281
3229 # we'll need this later 3282 # we'll need this later
3230 targetsubs = sorted(s for s in wctx.substate if m(s)) 3283 targetsubs = sorted(s for s in wctx.substate if m(s))
3239 def badfn(path, msg): 3292 def badfn(path, msg):
3240 if path in names: 3293 if path in names:
3241 return 3294 return
3242 if path in ctx.substate: 3295 if path in ctx.substate:
3243 return 3296 return
3244 path_ = path + '/' 3297 path_ = path + b'/'
3245 for f in names: 3298 for f in names:
3246 if f.startswith(path_): 3299 if f.startswith(path_):
3247 return 3300 return
3248 ui.warn("%s: %s\n" % (uipathfn(path), msg)) 3301 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3249 3302
3250 for abs in ctx.walk(matchmod.badmatch(m, badfn)): 3303 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3251 if abs not in names: 3304 if abs not in names:
3252 names[abs] = m.exact(abs) 3305 names[abs] = m.exact(abs)
3253 3306
3324 3377
3325 # if f is a rename, update `names` to also revert the source 3378 # if f is a rename, update `names` to also revert the source
3326 for f in localchanges: 3379 for f in localchanges:
3327 src = repo.dirstate.copied(f) 3380 src = repo.dirstate.copied(f)
3328 # XXX should we check for rename down to target node? 3381 # XXX should we check for rename down to target node?
3329 if src and src not in names and repo.dirstate[src] == 'r': 3382 if src and src not in names and repo.dirstate[src] == b'r':
3330 dsremoved.add(src) 3383 dsremoved.add(src)
3331 names[src] = True 3384 names[src] = True
3332 3385
3333 # determine the exact nature of the deleted changesets 3386 # determine the exact nature of the deleted changesets
3334 deladded = set(_deleted) 3387 deladded = set(_deleted)
3338 deleted = _deleted - deladded 3391 deleted = _deleted - deladded
3339 3392
3340 # distinguish between file to forget and the other 3393 # distinguish between file to forget and the other
3341 added = set() 3394 added = set()
3342 for abs in dsadded: 3395 for abs in dsadded:
3343 if repo.dirstate[abs] != 'a': 3396 if repo.dirstate[abs] != b'a':
3344 added.add(abs) 3397 added.add(abs)
3345 dsadded -= added 3398 dsadded -= added
3346 3399
3347 for abs in deladded: 3400 for abs in deladded:
3348 if repo.dirstate[abs] == 'a': 3401 if repo.dirstate[abs] == b'a':
3349 dsadded.add(abs) 3402 dsadded.add(abs)
3350 deladded -= dsadded 3403 deladded -= dsadded
3351 3404
3352 # For files marked as removed, we check if an unknown file is present at 3405 # For files marked as removed, we check if an unknown file is present at
3353 # the same path. If a such file exists it may need to be backed up. 3406 # the same path. If a such file exists it may need to be backed up.
3368 dsremoved -= dsremovunk 3421 dsremoved -= dsremovunk
3369 3422
3370 # action to be actually performed by revert 3423 # action to be actually performed by revert
3371 # (<list of file>, message>) tuple 3424 # (<list of file>, message>) tuple
3372 actions = { 3425 actions = {
3373 'revert': ([], _('reverting %s\n')), 3426 b'revert': ([], _(b'reverting %s\n')),
3374 'add': ([], _('adding %s\n')), 3427 b'add': ([], _(b'adding %s\n')),
3375 'remove': ([], _('removing %s\n')), 3428 b'remove': ([], _(b'removing %s\n')),
3376 'drop': ([], _('removing %s\n')), 3429 b'drop': ([], _(b'removing %s\n')),
3377 'forget': ([], _('forgetting %s\n')), 3430 b'forget': ([], _(b'forgetting %s\n')),
3378 'undelete': ([], _('undeleting %s\n')), 3431 b'undelete': ([], _(b'undeleting %s\n')),
3379 'noop': (None, _('no changes needed to %s\n')), 3432 b'noop': (None, _(b'no changes needed to %s\n')),
3380 'unknown': (None, _('file not managed: %s\n')), 3433 b'unknown': (None, _(b'file not managed: %s\n')),
3381 } 3434 }
3382 3435
3383 # "constant" that convey the backup strategy. 3436 # "constant" that convey the backup strategy.
3384 # All set to `discard` if `no-backup` is set do avoid checking 3437 # All set to `discard` if `no-backup` is set do avoid checking
3385 # no_backup lower in the code. 3438 # no_backup lower in the code.
3386 # These values are ordered for comparison purposes 3439 # These values are ordered for comparison purposes
3387 backupinteractive = 3 # do backup if interactively modified 3440 backupinteractive = 3 # do backup if interactively modified
3388 backup = 2 # unconditionally do backup 3441 backup = 2 # unconditionally do backup
3389 check = 1 # check if the existing file differs from target 3442 check = 1 # check if the existing file differs from target
3390 discard = 0 # never do backup 3443 discard = 0 # never do backup
3391 if opts.get('no_backup'): 3444 if opts.get(b'no_backup'):
3392 backupinteractive = backup = check = discard 3445 backupinteractive = backup = check = discard
3393 if interactive: 3446 if interactive:
3394 dsmodifiedbackup = backupinteractive 3447 dsmodifiedbackup = backupinteractive
3395 else: 3448 else:
3396 dsmodifiedbackup = backup 3449 dsmodifiedbackup = backup
3397 tobackup = set() 3450 tobackup = set()
3398 3451
3399 backupanddel = actions['remove'] 3452 backupanddel = actions[b'remove']
3400 if not opts.get('no_backup'): 3453 if not opts.get(b'no_backup'):
3401 backupanddel = actions['drop'] 3454 backupanddel = actions[b'drop']
3402 3455
3403 disptable = ( 3456 disptable = (
3404 # dispatch table: 3457 # dispatch table:
3405 # file state 3458 # file state
3406 # action 3459 # action
3407 # make backup 3460 # make backup
3408 ## Sets that results that will change file on disk 3461 ## Sets that results that will change file on disk
3409 # Modified compared to target, no local change 3462 # Modified compared to target, no local change
3410 (modified, actions['revert'], discard), 3463 (modified, actions[b'revert'], discard),
3411 # Modified compared to target, but local file is deleted 3464 # Modified compared to target, but local file is deleted
3412 (deleted, actions['revert'], discard), 3465 (deleted, actions[b'revert'], discard),
3413 # Modified compared to target, local change 3466 # Modified compared to target, local change
3414 (dsmodified, actions['revert'], dsmodifiedbackup), 3467 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3415 # Added since target 3468 # Added since target
3416 (added, actions['remove'], discard), 3469 (added, actions[b'remove'], discard),
3417 # Added in working directory 3470 # Added in working directory
3418 (dsadded, actions['forget'], discard), 3471 (dsadded, actions[b'forget'], discard),
3419 # Added since target, have local modification 3472 # Added since target, have local modification
3420 (modadded, backupanddel, backup), 3473 (modadded, backupanddel, backup),
3421 # Added since target but file is missing in working directory 3474 # Added since target but file is missing in working directory
3422 (deladded, actions['drop'], discard), 3475 (deladded, actions[b'drop'], discard),
3423 # Removed since target, before working copy parent 3476 # Removed since target, before working copy parent
3424 (removed, actions['add'], discard), 3477 (removed, actions[b'add'], discard),
3425 # Same as `removed` but an unknown file exists at the same path 3478 # Same as `removed` but an unknown file exists at the same path
3426 (removunk, actions['add'], check), 3479 (removunk, actions[b'add'], check),
3427 # Removed since targe, marked as such in working copy parent 3480 # Removed since targe, marked as such in working copy parent
3428 (dsremoved, actions['undelete'], discard), 3481 (dsremoved, actions[b'undelete'], discard),
3429 # Same as `dsremoved` but an unknown file exists at the same path 3482 # Same as `dsremoved` but an unknown file exists at the same path
3430 (dsremovunk, actions['undelete'], check), 3483 (dsremovunk, actions[b'undelete'], check),
3431 ## the following sets does not result in any file changes 3484 ## the following sets does not result in any file changes
3432 # File with no modification 3485 # File with no modification
3433 (clean, actions['noop'], discard), 3486 (clean, actions[b'noop'], discard),
3434 # Existing file, not tracked anywhere 3487 # Existing file, not tracked anywhere
3435 (unknown, actions['unknown'], discard), 3488 (unknown, actions[b'unknown'], discard),
3436 ) 3489 )
3437 3490
3438 for abs, exact in sorted(names.items()): 3491 for abs, exact in sorted(names.items()):
3439 # target file to be touch on disk (relative to cwd) 3492 # target file to be touch on disk (relative to cwd)
3440 target = repo.wjoin(abs) 3493 target = repo.wjoin(abs)
3455 absbakname = scmutil.backuppath(ui, repo, abs) 3508 absbakname = scmutil.backuppath(ui, repo, abs)
3456 bakname = os.path.relpath( 3509 bakname = os.path.relpath(
3457 absbakname, start=repo.root 3510 absbakname, start=repo.root
3458 ) 3511 )
3459 ui.note( 3512 ui.note(
3460 _('saving current version of %s as %s\n') 3513 _(b'saving current version of %s as %s\n')
3461 % (uipathfn(abs), uipathfn(bakname)) 3514 % (uipathfn(abs), uipathfn(bakname))
3462 ) 3515 )
3463 if not opts.get('dry_run'): 3516 if not opts.get(b'dry_run'):
3464 if interactive: 3517 if interactive:
3465 util.copyfile(target, absbakname) 3518 util.copyfile(target, absbakname)
3466 else: 3519 else:
3467 util.rename(target, absbakname) 3520 util.rename(target, absbakname)
3468 if opts.get('dry_run'): 3521 if opts.get(b'dry_run'):
3469 if ui.verbose or not exact: 3522 if ui.verbose or not exact:
3470 ui.status(msg % uipathfn(abs)) 3523 ui.status(msg % uipathfn(abs))
3471 elif exact: 3524 elif exact:
3472 ui.warn(msg % uipathfn(abs)) 3525 ui.warn(msg % uipathfn(abs))
3473 break 3526 break
3474 3527
3475 if not opts.get('dry_run'): 3528 if not opts.get(b'dry_run'):
3476 needdata = ('revert', 'add', 'undelete') 3529 needdata = (b'revert', b'add', b'undelete')
3477 oplist = [actions[name][0] for name in needdata] 3530 oplist = [actions[name][0] for name in needdata]
3478 prefetch = scmutil.prefetchfiles 3531 prefetch = scmutil.prefetchfiles
3479 matchfiles = scmutil.matchfiles 3532 matchfiles = scmutil.matchfiles
3480 prefetch( 3533 prefetch(
3481 repo, 3534 repo,
3502 wctx.sub(sub).revert( 3555 wctx.sub(sub).revert(
3503 ctx.substate[sub], *pats, **pycompat.strkwargs(opts) 3556 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3504 ) 3557 )
3505 except KeyError: 3558 except KeyError:
3506 raise error.Abort( 3559 raise error.Abort(
3507 "subrepository '%s' does not exist in %s!" 3560 b"subrepository '%s' does not exist in %s!"
3508 % (sub, short(ctx.node())) 3561 % (sub, short(ctx.node()))
3509 ) 3562 )
3510 3563
3511 3564
3512 def _performrevert( 3565 def _performrevert(
3535 fc = ctx[f] 3588 fc = ctx[f]
3536 repo.wwrite(f, fc.data(), fc.flags()) 3589 repo.wwrite(f, fc.data(), fc.flags())
3537 3590
3538 def doremove(f): 3591 def doremove(f):
3539 try: 3592 try:
3540 rmdir = repo.ui.configbool('experimental', 'removeemptydirs') 3593 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3541 repo.wvfs.unlinkpath(f, rmdir=rmdir) 3594 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3542 except OSError: 3595 except OSError:
3543 pass 3596 pass
3544 repo.dirstate.remove(f) 3597 repo.dirstate.remove(f)
3545 3598
3547 exact = names[f] 3600 exact = names[f]
3548 if repo.ui.verbose or not exact: 3601 if repo.ui.verbose or not exact:
3549 repo.ui.status(actions[action][1] % uipathfn(f)) 3602 repo.ui.status(actions[action][1] % uipathfn(f))
3550 3603
3551 audit_path = pathutil.pathauditor(repo.root, cached=True) 3604 audit_path = pathutil.pathauditor(repo.root, cached=True)
3552 for f in actions['forget'][0]: 3605 for f in actions[b'forget'][0]:
3553 if interactive: 3606 if interactive:
3554 choice = repo.ui.promptchoice( 3607 choice = repo.ui.promptchoice(
3555 _("forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f) 3608 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3556 ) 3609 )
3557 if choice == 0: 3610 if choice == 0:
3558 prntstatusmsg('forget', f) 3611 prntstatusmsg(b'forget', f)
3559 repo.dirstate.drop(f) 3612 repo.dirstate.drop(f)
3560 else: 3613 else:
3561 excluded_files.append(f) 3614 excluded_files.append(f)
3562 else: 3615 else:
3563 prntstatusmsg('forget', f) 3616 prntstatusmsg(b'forget', f)
3564 repo.dirstate.drop(f) 3617 repo.dirstate.drop(f)
3565 for f in actions['remove'][0]: 3618 for f in actions[b'remove'][0]:
3566 audit_path(f) 3619 audit_path(f)
3567 if interactive: 3620 if interactive:
3568 choice = repo.ui.promptchoice( 3621 choice = repo.ui.promptchoice(
3569 _("remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f) 3622 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3570 ) 3623 )
3571 if choice == 0: 3624 if choice == 0:
3572 prntstatusmsg('remove', f) 3625 prntstatusmsg(b'remove', f)
3573 doremove(f) 3626 doremove(f)
3574 else: 3627 else:
3575 excluded_files.append(f) 3628 excluded_files.append(f)
3576 else: 3629 else:
3577 prntstatusmsg('remove', f) 3630 prntstatusmsg(b'remove', f)
3578 doremove(f) 3631 doremove(f)
3579 for f in actions['drop'][0]: 3632 for f in actions[b'drop'][0]:
3580 audit_path(f) 3633 audit_path(f)
3581 prntstatusmsg('drop', f) 3634 prntstatusmsg(b'drop', f)
3582 repo.dirstate.remove(f) 3635 repo.dirstate.remove(f)
3583 3636
3584 normal = None 3637 normal = None
3585 if node == parent: 3638 if node == parent:
3586 # We're reverting to our parent. If possible, we'd like status 3639 # We're reverting to our parent. If possible, we'd like status
3592 normal = repo.dirstate.normal 3645 normal = repo.dirstate.normal
3593 3646
3594 newlyaddedandmodifiedfiles = set() 3647 newlyaddedandmodifiedfiles = set()
3595 if interactive: 3648 if interactive:
3596 # Prompt the user for changes to revert 3649 # Prompt the user for changes to revert
3597 torevert = [f for f in actions['revert'][0] if f not in excluded_files] 3650 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3598 m = scmutil.matchfiles(repo, torevert) 3651 m = scmutil.matchfiles(repo, torevert)
3599 diffopts = patch.difffeatureopts( 3652 diffopts = patch.difffeatureopts(
3600 repo.ui, 3653 repo.ui,
3601 whitespace=True, 3654 whitespace=True,
3602 section='commands', 3655 section=b'commands',
3603 configprefix='revert.interactive.', 3656 configprefix=b'revert.interactive.',
3604 ) 3657 )
3605 diffopts.nodates = True 3658 diffopts.nodates = True
3606 diffopts.git = True 3659 diffopts.git = True
3607 operation = 'apply' 3660 operation = b'apply'
3608 if node == parent: 3661 if node == parent:
3609 if repo.ui.configbool( 3662 if repo.ui.configbool(
3610 'experimental', 'revert.interactive.select-to-keep' 3663 b'experimental', b'revert.interactive.select-to-keep'
3611 ): 3664 ):
3612 operation = 'keep' 3665 operation = b'keep'
3613 else: 3666 else:
3614 operation = 'discard' 3667 operation = b'discard'
3615 3668
3616 if operation == 'apply': 3669 if operation == b'apply':
3617 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts) 3670 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3618 else: 3671 else:
3619 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts) 3672 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3620 originalchunks = patch.parsepatch(diff) 3673 originalchunks = patch.parsepatch(diff)
3621 3674
3622 try: 3675 try:
3623 3676
3624 chunks, opts = recordfilter( 3677 chunks, opts = recordfilter(
3625 repo.ui, originalchunks, match, operation=operation 3678 repo.ui, originalchunks, match, operation=operation
3626 ) 3679 )
3627 if operation == 'discard': 3680 if operation == b'discard':
3628 chunks = patch.reversehunks(chunks) 3681 chunks = patch.reversehunks(chunks)
3629 3682
3630 except error.PatchError as err: 3683 except error.PatchError as err:
3631 raise error.Abort(_('error parsing patch: %s') % err) 3684 raise error.Abort(_(b'error parsing patch: %s') % err)
3632 3685
3633 # FIXME: when doing an interactive revert of a copy, there's no way of 3686 # FIXME: when doing an interactive revert of a copy, there's no way of
3634 # performing a partial revert of the added file, the only option is 3687 # performing a partial revert of the added file, the only option is
3635 # "remove added file <name> (Yn)?", so we don't need to worry about the 3688 # "remove added file <name> (Yn)?", so we don't need to worry about the
3636 # alsorestore value. Ideally we'd be able to partially revert 3689 # alsorestore value. Ideally we'd be able to partially revert
3642 tobackup = set() 3695 tobackup = set()
3643 # Apply changes 3696 # Apply changes
3644 fp = stringio() 3697 fp = stringio()
3645 # chunks are serialized per file, but files aren't sorted 3698 # chunks are serialized per file, but files aren't sorted
3646 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))): 3699 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3647 prntstatusmsg('revert', f) 3700 prntstatusmsg(b'revert', f)
3648 files = set() 3701 files = set()
3649 for c in chunks: 3702 for c in chunks:
3650 if ishunk(c): 3703 if ishunk(c):
3651 abs = c.header.filename() 3704 abs = c.header.filename()
3652 # Create a backup file only if this hunk should be backed up 3705 # Create a backup file only if this hunk should be backed up
3655 bakname = scmutil.backuppath(repo.ui, repo, abs) 3708 bakname = scmutil.backuppath(repo.ui, repo, abs)
3656 util.copyfile(target, bakname) 3709 util.copyfile(target, bakname)
3657 tobackup.remove(abs) 3710 tobackup.remove(abs)
3658 if abs not in files: 3711 if abs not in files:
3659 files.add(abs) 3712 files.add(abs)
3660 if operation == 'keep': 3713 if operation == b'keep':
3661 checkout(abs) 3714 checkout(abs)
3662 c.write(fp) 3715 c.write(fp)
3663 dopatch = fp.tell() 3716 dopatch = fp.tell()
3664 fp.seek(0) 3717 fp.seek(0)
3665 if dopatch: 3718 if dopatch:
3667 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None) 3720 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3668 except error.PatchError as err: 3721 except error.PatchError as err:
3669 raise error.Abort(pycompat.bytestr(err)) 3722 raise error.Abort(pycompat.bytestr(err))
3670 del fp 3723 del fp
3671 else: 3724 else:
3672 for f in actions['revert'][0]: 3725 for f in actions[b'revert'][0]:
3673 prntstatusmsg('revert', f) 3726 prntstatusmsg(b'revert', f)
3674 checkout(f) 3727 checkout(f)
3675 if normal: 3728 if normal:
3676 normal(f) 3729 normal(f)
3677 3730
3678 for f in actions['add'][0]: 3731 for f in actions[b'add'][0]:
3679 # Don't checkout modified files, they are already created by the diff 3732 # Don't checkout modified files, they are already created by the diff
3680 if f not in newlyaddedandmodifiedfiles: 3733 if f not in newlyaddedandmodifiedfiles:
3681 prntstatusmsg('add', f) 3734 prntstatusmsg(b'add', f)
3682 checkout(f) 3735 checkout(f)
3683 repo.dirstate.add(f) 3736 repo.dirstate.add(f)
3684 3737
3685 normal = repo.dirstate.normallookup 3738 normal = repo.dirstate.normallookup
3686 if node == parent and p2 == nullid: 3739 if node == parent and p2 == nullid:
3687 normal = repo.dirstate.normal 3740 normal = repo.dirstate.normal
3688 for f in actions['undelete'][0]: 3741 for f in actions[b'undelete'][0]:
3689 if interactive: 3742 if interactive:
3690 choice = repo.ui.promptchoice( 3743 choice = repo.ui.promptchoice(
3691 _("add back removed file %s (Yn)?$$ &Yes $$ &No") % f 3744 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3692 ) 3745 )
3693 if choice == 0: 3746 if choice == 0:
3694 prntstatusmsg('undelete', f) 3747 prntstatusmsg(b'undelete', f)
3695 checkout(f) 3748 checkout(f)
3696 normal(f) 3749 normal(f)
3697 else: 3750 else:
3698 excluded_files.append(f) 3751 excluded_files.append(f)
3699 else: 3752 else:
3700 prntstatusmsg('undelete', f) 3753 prntstatusmsg(b'undelete', f)
3701 checkout(f) 3754 checkout(f)
3702 normal(f) 3755 normal(f)
3703 3756
3704 copied = copies.pathcopies(repo[parent], ctx) 3757 copied = copies.pathcopies(repo[parent], ctx)
3705 3758
3706 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]: 3759 for f in (
3760 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3761 ):
3707 if f in copied: 3762 if f in copied:
3708 repo.dirstate.copy(copied[f], f) 3763 repo.dirstate.copy(copied[f], f)
3709 3764
3710 3765
3711 # a list of (ui, repo, otherpeer, opts, missing) functions called by 3766 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3746 3801
3747 for s in statemod._unfinishedstates: 3802 for s in statemod._unfinishedstates:
3748 if ( 3803 if (
3749 not s._clearable 3804 not s._clearable
3750 or (commit and s._allowcommit) 3805 or (commit and s._allowcommit)
3751 or (s._opname == 'merge' and skipmerge) 3806 or (s._opname == b'merge' and skipmerge)
3752 or s._reportonly 3807 or s._reportonly
3753 ): 3808 ):
3754 continue 3809 continue
3755 if s.isunfinished(repo): 3810 if s.isunfinished(repo):
3756 raise error.Abort(s.msg(), hint=s.hint()) 3811 raise error.Abort(s.msg(), hint=s.hint())
3765 continue 3820 continue
3766 if not state._clearable and state.isunfinished(repo): 3821 if not state._clearable and state.isunfinished(repo):
3767 raise error.Abort(state.msg(), hint=state.hint()) 3822 raise error.Abort(state.msg(), hint=state.hint())
3768 3823
3769 for s in statemod._unfinishedstates: 3824 for s in statemod._unfinishedstates:
3770 if s._opname == 'merge' or state._reportonly: 3825 if s._opname == b'merge' or state._reportonly:
3771 continue 3826 continue
3772 if s._clearable and s.isunfinished(repo): 3827 if s._clearable and s.isunfinished(repo):
3773 util.unlink(repo.vfs.join(s._fname)) 3828 util.unlink(repo.vfs.join(s._fname))
3774 3829
3775 3830
3791 continue is supported by the operation. 3846 continue is supported by the operation.
3792 3847
3793 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is 3848 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3794 a boolean. 3849 a boolean.
3795 ''' 3850 '''
3796 contmsg = _("continue: %s") 3851 contmsg = _(b"continue: %s")
3797 for state in statemod._unfinishedstates: 3852 for state in statemod._unfinishedstates:
3798 if not state._continueflag: 3853 if not state._continueflag:
3799 continue 3854 continue
3800 if state.isunfinished(repo): 3855 if state.isunfinished(repo):
3801 return contmsg % state.continuemsg(), True 3856 return contmsg % state.continuemsg(), True
3802 if repo[None].dirty(missing=True, merge=False, branch=False): 3857 if repo[None].dirty(missing=True, merge=False, branch=False):
3803 return contmsg % _("hg commit"), False 3858 return contmsg % _(b"hg commit"), False
3804 return None, None 3859 return None, None
3805 3860
3806 3861
3807 def checkafterresolved(repo): 3862 def checkafterresolved(repo):
3808 '''Inform the user about the next action after completing hg resolve 3863 '''Inform the user about the next action after completing hg resolve
3813 Otherwise, it will yield repo.ui.note. 3868 Otherwise, it will yield repo.ui.note.
3814 ''' 3869 '''
3815 msg, warning = howtocontinue(repo) 3870 msg, warning = howtocontinue(repo)
3816 if msg is not None: 3871 if msg is not None:
3817 if warning: 3872 if warning:
3818 repo.ui.warn("%s\n" % msg) 3873 repo.ui.warn(b"%s\n" % msg)
3819 else: 3874 else:
3820 repo.ui.note("%s\n" % msg) 3875 repo.ui.note(b"%s\n" % msg)
3821 3876
3822 3877
3823 def wrongtooltocontinue(repo, task): 3878 def wrongtooltocontinue(repo, task):
3824 '''Raise an abort suggesting how to properly continue if there is an 3879 '''Raise an abort suggesting how to properly continue if there is an
3825 active task. 3880 active task.
3831 ''' 3886 '''
3832 after = howtocontinue(repo) 3887 after = howtocontinue(repo)
3833 hint = None 3888 hint = None
3834 if after[1]: 3889 if after[1]:
3835 hint = after[0] 3890 hint = after[0]
3836 raise error.Abort(_('no %s in progress') % task, hint=hint) 3891 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3837 3892
3838 3893
3839 def abortgraft(ui, repo, graftstate): 3894 def abortgraft(ui, repo, graftstate):
3840 """abort the interrupted graft and rollbacks to the state before interrupted 3895 """abort the interrupted graft and rollbacks to the state before interrupted
3841 graft""" 3896 graft"""
3842 if not graftstate.exists(): 3897 if not graftstate.exists():
3843 raise error.Abort(_("no interrupted graft to abort")) 3898 raise error.Abort(_(b"no interrupted graft to abort"))
3844 statedata = readgraftstate(repo, graftstate) 3899 statedata = readgraftstate(repo, graftstate)
3845 newnodes = statedata.get('newnodes') 3900 newnodes = statedata.get(b'newnodes')
3846 if newnodes is None: 3901 if newnodes is None:
3847 # and old graft state which does not have all the data required to abort 3902 # and old graft state which does not have all the data required to abort
3848 # the graft 3903 # the graft
3849 raise error.Abort(_("cannot abort using an old graftstate")) 3904 raise error.Abort(_(b"cannot abort using an old graftstate"))
3850 3905
3851 # changeset from which graft operation was started 3906 # changeset from which graft operation was started
3852 if len(newnodes) > 0: 3907 if len(newnodes) > 0:
3853 startctx = repo[newnodes[0]].p1() 3908 startctx = repo[newnodes[0]].p1()
3854 else: 3909 else:
3855 startctx = repo['.'] 3910 startctx = repo[b'.']
3856 # whether to strip or not 3911 # whether to strip or not
3857 cleanup = False 3912 cleanup = False
3858 from . import hg 3913 from . import hg
3859 3914
3860 if newnodes: 3915 if newnodes:
3862 cleanup = True 3917 cleanup = True
3863 # checking that none of the newnodes turned public or is public 3918 # checking that none of the newnodes turned public or is public
3864 immutable = [c for c in newnodes if not repo[c].mutable()] 3919 immutable = [c for c in newnodes if not repo[c].mutable()]
3865 if immutable: 3920 if immutable:
3866 repo.ui.warn( 3921 repo.ui.warn(
3867 _("cannot clean up public changesets %s\n") 3922 _(b"cannot clean up public changesets %s\n")
3868 % ', '.join(bytes(repo[r]) for r in immutable), 3923 % b', '.join(bytes(repo[r]) for r in immutable),
3869 hint=_("see 'hg help phases' for details"), 3924 hint=_(b"see 'hg help phases' for details"),
3870 ) 3925 )
3871 cleanup = False 3926 cleanup = False
3872 3927
3873 # checking that no new nodes are created on top of grafted revs 3928 # checking that no new nodes are created on top of grafted revs
3874 desc = set(repo.changelog.descendants(newnodes)) 3929 desc = set(repo.changelog.descendants(newnodes))
3875 if desc - set(newnodes): 3930 if desc - set(newnodes):
3876 repo.ui.warn( 3931 repo.ui.warn(
3877 _( 3932 _(
3878 "new changesets detected on destination " 3933 b"new changesets detected on destination "
3879 "branch, can't strip\n" 3934 b"branch, can't strip\n"
3880 ) 3935 )
3881 ) 3936 )
3882 cleanup = False 3937 cleanup = False
3883 3938
3884 if cleanup: 3939 if cleanup:
3885 with repo.wlock(), repo.lock(): 3940 with repo.wlock(), repo.lock():
3886 hg.updaterepo(repo, startctx.node(), overwrite=True) 3941 hg.updaterepo(repo, startctx.node(), overwrite=True)
3887 # stripping the new nodes created 3942 # stripping the new nodes created
3888 strippoints = [ 3943 strippoints = [
3889 c.node() for c in repo.set("roots(%ld)", newnodes) 3944 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3890 ] 3945 ]
3891 repair.strip(repo.ui, repo, strippoints, backup=False) 3946 repair.strip(repo.ui, repo, strippoints, backup=False)
3892 3947
3893 if not cleanup: 3948 if not cleanup:
3894 # we don't update to the startnode if we can't strip 3949 # we don't update to the startnode if we can't strip
3895 startctx = repo['.'] 3950 startctx = repo[b'.']
3896 hg.updaterepo(repo, startctx.node(), overwrite=True) 3951 hg.updaterepo(repo, startctx.node(), overwrite=True)
3897 3952
3898 ui.status(_("graft aborted\n")) 3953 ui.status(_(b"graft aborted\n"))
3899 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12]) 3954 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3900 graftstate.delete() 3955 graftstate.delete()
3901 return 0 3956 return 0
3902 3957
3903 3958
3904 def readgraftstate(repo, graftstate): 3959 def readgraftstate(repo, graftstate):
3905 """read the graft state file and return a dict of the data stored in it""" 3960 """read the graft state file and return a dict of the data stored in it"""
3906 try: 3961 try:
3907 return graftstate.read() 3962 return graftstate.read()
3908 except error.CorruptedState: 3963 except error.CorruptedState:
3909 nodes = repo.vfs.read('graftstate').splitlines() 3964 nodes = repo.vfs.read(b'graftstate').splitlines()
3910 return {'nodes': nodes} 3965 return {b'nodes': nodes}
3911 3966
3912 3967
3913 def hgabortgraft(ui, repo): 3968 def hgabortgraft(ui, repo):
3914 """ abort logic for aborting graft using 'hg abort'""" 3969 """ abort logic for aborting graft using 'hg abort'"""
3915 with repo.wlock(): 3970 with repo.wlock():
3916 graftstate = statemod.cmdstate(repo, 'graftstate') 3971 graftstate = statemod.cmdstate(repo, b'graftstate')
3917 return abortgraft(ui, repo, graftstate) 3972 return abortgraft(ui, repo, graftstate)