Mercurial > public > mercurial-scm > hg
comparison mercurial/subrepo.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 |
---|---|
82 raise ex | 82 raise ex |
83 except error.Abort as ex: | 83 except error.Abort as ex: |
84 subrepo = subrelpath(self) | 84 subrepo = subrelpath(self) |
85 errormsg = ( | 85 errormsg = ( |
86 stringutil.forcebytestr(ex) | 86 stringutil.forcebytestr(ex) |
87 + ' ' | 87 + b' ' |
88 + _('(in subrepository "%s")') % subrepo | 88 + _(b'(in subrepository "%s")') % subrepo |
89 ) | 89 ) |
90 # avoid handling this exception by raising a SubrepoAbort exception | 90 # avoid handling this exception by raising a SubrepoAbort exception |
91 raise SubrepoAbort( | 91 raise SubrepoAbort( |
92 errormsg, hint=ex.hint, subrepo=subrepo, cause=sys.exc_info() | 92 errormsg, hint=ex.hint, subrepo=subrepo, cause=sys.exc_info() |
93 ) | 93 ) |
97 | 97 |
98 | 98 |
99 def _updateprompt(ui, sub, dirty, local, remote): | 99 def _updateprompt(ui, sub, dirty, local, remote): |
100 if dirty: | 100 if dirty: |
101 msg = _( | 101 msg = _( |
102 ' subrepository sources for %s differ\n' | 102 b' subrepository sources for %s differ\n' |
103 'you can use (l)ocal source (%s) or (r)emote source (%s).\n' | 103 b'you can use (l)ocal source (%s) or (r)emote source (%s).\n' |
104 'what do you want to do?' | 104 b'what do you want to do?' |
105 '$$ &Local $$ &Remote' | 105 b'$$ &Local $$ &Remote' |
106 ) % (subrelpath(sub), local, remote) | 106 ) % (subrelpath(sub), local, remote) |
107 else: | 107 else: |
108 msg = _( | 108 msg = _( |
109 ' subrepository sources for %s differ (in checked out ' | 109 b' subrepository sources for %s differ (in checked out ' |
110 'version)\n' | 110 b'version)\n' |
111 'you can use (l)ocal source (%s) or (r)emote source (%s).\n' | 111 b'you can use (l)ocal source (%s) or (r)emote source (%s).\n' |
112 'what do you want to do?' | 112 b'what do you want to do?' |
113 '$$ &Local $$ &Remote' | 113 b'$$ &Local $$ &Remote' |
114 ) % (subrelpath(sub), local, remote) | 114 ) % (subrelpath(sub), local, remote) |
115 return ui.promptchoice(msg, 0) | 115 return ui.promptchoice(msg, 0) |
116 | 116 |
117 | 117 |
118 def _sanitize(ui, vfs, ignore): | 118 def _sanitize(ui, vfs, ignore): |
119 for dirname, dirs, names in vfs.walk(): | 119 for dirname, dirs, names in vfs.walk(): |
120 for i, d in enumerate(dirs): | 120 for i, d in enumerate(dirs): |
121 if d.lower() == ignore: | 121 if d.lower() == ignore: |
122 del dirs[i] | 122 del dirs[i] |
123 break | 123 break |
124 if vfs.basename(dirname).lower() != '.hg': | 124 if vfs.basename(dirname).lower() != b'.hg': |
125 continue | 125 continue |
126 for f in names: | 126 for f in names: |
127 if f.lower() == 'hgrc': | 127 if f.lower() == b'hgrc': |
128 ui.warn( | 128 ui.warn( |
129 _( | 129 _( |
130 "warning: removing potentially hostile 'hgrc' " | 130 b"warning: removing potentially hostile 'hgrc' " |
131 "in '%s'\n" | 131 b"in '%s'\n" |
132 ) | 132 ) |
133 % vfs.join(dirname) | 133 % vfs.join(dirname) |
134 ) | 134 ) |
135 vfs.unlink(vfs.reljoin(dirname, f)) | 135 vfs.unlink(vfs.reljoin(dirname, f)) |
136 | 136 |
137 | 137 |
138 def _auditsubrepopath(repo, path): | 138 def _auditsubrepopath(repo, path): |
139 # sanity check for potentially unsafe paths such as '~' and '$FOO' | 139 # sanity check for potentially unsafe paths such as '~' and '$FOO' |
140 if path.startswith('~') or '$' in path or util.expandpath(path) != path: | 140 if path.startswith(b'~') or b'$' in path or util.expandpath(path) != path: |
141 raise error.Abort( | 141 raise error.Abort( |
142 _('subrepo path contains illegal component: %s') % path | 142 _(b'subrepo path contains illegal component: %s') % path |
143 ) | 143 ) |
144 # auditor doesn't check if the path itself is a symlink | 144 # auditor doesn't check if the path itself is a symlink |
145 pathutil.pathauditor(repo.root)(path) | 145 pathutil.pathauditor(repo.root)(path) |
146 if repo.wvfs.islink(path): | 146 if repo.wvfs.islink(path): |
147 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path) | 147 raise error.Abort(_(b"subrepo '%s' traverses symbolic link") % path) |
148 | 148 |
149 | 149 |
150 SUBREPO_ALLOWED_DEFAULTS = { | 150 SUBREPO_ALLOWED_DEFAULTS = { |
151 'hg': True, | 151 b'hg': True, |
152 'git': False, | 152 b'git': False, |
153 'svn': False, | 153 b'svn': False, |
154 } | 154 } |
155 | 155 |
156 | 156 |
157 def _checktype(ui, kind): | 157 def _checktype(ui, kind): |
158 # subrepos.allowed is a master kill switch. If disabled, subrepos are | 158 # subrepos.allowed is a master kill switch. If disabled, subrepos are |
159 # disabled period. | 159 # disabled period. |
160 if not ui.configbool('subrepos', 'allowed', True): | 160 if not ui.configbool(b'subrepos', b'allowed', True): |
161 raise error.Abort( | 161 raise error.Abort( |
162 _('subrepos not enabled'), | 162 _(b'subrepos not enabled'), |
163 hint=_("see 'hg help config.subrepos' for details"), | 163 hint=_(b"see 'hg help config.subrepos' for details"), |
164 ) | 164 ) |
165 | 165 |
166 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False) | 166 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False) |
167 if not ui.configbool('subrepos', '%s:allowed' % kind, default): | 167 if not ui.configbool(b'subrepos', b'%s:allowed' % kind, default): |
168 raise error.Abort( | 168 raise error.Abort( |
169 _('%s subrepos not allowed') % kind, | 169 _(b'%s subrepos not allowed') % kind, |
170 hint=_("see 'hg help config.subrepos' for details"), | 170 hint=_(b"see 'hg help config.subrepos' for details"), |
171 ) | 171 ) |
172 | 172 |
173 if kind not in types: | 173 if kind not in types: |
174 raise error.Abort(_('unknown subrepo type %s') % kind) | 174 raise error.Abort(_(b'unknown subrepo type %s') % kind) |
175 | 175 |
176 | 176 |
177 def subrepo(ctx, path, allowwdir=False, allowcreate=True): | 177 def subrepo(ctx, path, allowwdir=False, allowcreate=True): |
178 """return instance of the right subrepo class for subrepo in path""" | 178 """return instance of the right subrepo class for subrepo in path""" |
179 # subrepo inherently violates our import layering rules | 179 # subrepo inherently violates our import layering rules |
207 | 207 |
208 repo = ctx.repo() | 208 repo = ctx.repo() |
209 _auditsubrepopath(repo, path) | 209 _auditsubrepopath(repo, path) |
210 state = ctx.substate[path] | 210 state = ctx.substate[path] |
211 _checktype(repo.ui, state[2]) | 211 _checktype(repo.ui, state[2]) |
212 subrev = '' | 212 subrev = b'' |
213 if state[2] == 'hg': | 213 if state[2] == b'hg': |
214 subrev = "0" * 40 | 214 subrev = b"0" * 40 |
215 return types[state[2]](pctx, path, (state[0], subrev), True) | 215 return types[state[2]](pctx, path, (state[0], subrev), True) |
216 | 216 |
217 | 217 |
218 # subrepo classes need to implement the following abstract class: | 218 # subrepo classes need to implement the following abstract class: |
219 | 219 |
263 of exception. | 263 of exception. |
264 | 264 |
265 This returns None, otherwise. | 265 This returns None, otherwise. |
266 """ | 266 """ |
267 if self.dirty(ignoreupdate=ignoreupdate, missing=missing): | 267 if self.dirty(ignoreupdate=ignoreupdate, missing=missing): |
268 return _('uncommitted changes in subrepository "%s"') % subrelpath( | 268 return _(b'uncommitted changes in subrepository "%s"') % subrelpath( |
269 self | 269 self |
270 ) | 270 ) |
271 | 271 |
272 def bailifchanged(self, ignoreupdate=False, hint=None): | 272 def bailifchanged(self, ignoreupdate=False, hint=None): |
273 """raise Abort if subrepository is ``dirty()`` | 273 """raise Abort if subrepository is ``dirty()`` |
323 | 323 |
324 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): | 324 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): |
325 return [] | 325 return [] |
326 | 326 |
327 def addremove(self, matcher, prefix, uipathfn, opts): | 327 def addremove(self, matcher, prefix, uipathfn, opts): |
328 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported"))) | 328 self.ui.warn(b"%s: %s" % (prefix, _(b"addremove is not supported"))) |
329 return 1 | 329 return 1 |
330 | 330 |
331 def cat(self, match, fm, fntemplate, prefix, **opts): | 331 def cat(self, match, fm, fntemplate, prefix, **opts): |
332 return 1 | 332 return 1 |
333 | 333 |
351 """return file data, optionally passed through repo decoders""" | 351 """return file data, optionally passed through repo decoders""" |
352 raise NotImplementedError | 352 raise NotImplementedError |
353 | 353 |
354 def fileflags(self, name): | 354 def fileflags(self, name): |
355 """return file flags""" | 355 """return file flags""" |
356 return '' | 356 return b'' |
357 | 357 |
358 def matchfileset(self, expr, badfn=None): | 358 def matchfileset(self, expr, badfn=None): |
359 """Resolve the fileset expression for this repo""" | 359 """Resolve the fileset expression for this repo""" |
360 return matchmod.never(badfn=badfn) | 360 return matchmod.never(badfn=badfn) |
361 | 361 |
369 else: | 369 else: |
370 files = self.files() | 370 files = self.files() |
371 total = len(files) | 371 total = len(files) |
372 relpath = subrelpath(self) | 372 relpath = subrelpath(self) |
373 progress = self.ui.makeprogress( | 373 progress = self.ui.makeprogress( |
374 _('archiving (%s)') % relpath, unit=_('files'), total=total | 374 _(b'archiving (%s)') % relpath, unit=_(b'files'), total=total |
375 ) | 375 ) |
376 progress.update(0) | 376 progress.update(0) |
377 for name in files: | 377 for name in files: |
378 flags = self.fileflags(name) | 378 flags = self.fileflags(name) |
379 mode = 'x' in flags and 0o755 or 0o644 | 379 mode = b'x' in flags and 0o755 or 0o644 |
380 symlink = 'l' in flags | 380 symlink = b'l' in flags |
381 archiver.addfile( | 381 archiver.addfile( |
382 prefix + name, mode, symlink, self.filedata(name, decode) | 382 prefix + name, mode, symlink, self.filedata(name, decode) |
383 ) | 383 ) |
384 progress.increment() | 384 progress.increment() |
385 progress.complete() | 385 progress.complete() |
408 """remove the matched files from the subrepository and the filesystem, | 408 """remove the matched files from the subrepository and the filesystem, |
409 possibly by force and/or after the file has been removed from the | 409 possibly by force and/or after the file has been removed from the |
410 filesystem. Return 0 on success, 1 on any warning. | 410 filesystem. Return 0 on success, 1 on any warning. |
411 """ | 411 """ |
412 warnings.append( | 412 warnings.append( |
413 _("warning: removefiles not implemented (%s)") % self._path | 413 _(b"warning: removefiles not implemented (%s)") % self._path |
414 ) | 414 ) |
415 return 1 | 415 return 1 |
416 | 416 |
417 def revert(self, substate, *pats, **opts): | 417 def revert(self, substate, *pats, **opts): |
418 self.ui.warn( | 418 self.ui.warn( |
419 _('%s: reverting %s subrepos is unsupported\n') | 419 _(b'%s: reverting %s subrepos is unsupported\n') |
420 % (substate[0], substate[2]) | 420 % (substate[0], substate[2]) |
421 ) | 421 ) |
422 return [] | 422 return [] |
423 | 423 |
424 def shortid(self, revid): | 424 def shortid(self, revid): |
452 def __init__(self, ctx, path, state, allowcreate): | 452 def __init__(self, ctx, path, state, allowcreate): |
453 super(hgsubrepo, self).__init__(ctx, path) | 453 super(hgsubrepo, self).__init__(ctx, path) |
454 self._state = state | 454 self._state = state |
455 r = ctx.repo() | 455 r = ctx.repo() |
456 root = r.wjoin(util.localpath(path)) | 456 root = r.wjoin(util.localpath(path)) |
457 create = allowcreate and not r.wvfs.exists('%s/.hg' % path) | 457 create = allowcreate and not r.wvfs.exists(b'%s/.hg' % path) |
458 # repository constructor does expand variables in path, which is | 458 # repository constructor does expand variables in path, which is |
459 # unsafe since subrepo path might come from untrusted source. | 459 # unsafe since subrepo path might come from untrusted source. |
460 if os.path.realpath(util.expandpath(root)) != root: | 460 if os.path.realpath(util.expandpath(root)) != root: |
461 raise error.Abort( | 461 raise error.Abort( |
462 _('subrepo path contains illegal component: %s') % path | 462 _(b'subrepo path contains illegal component: %s') % path |
463 ) | 463 ) |
464 self._repo = hg.repository(r.baseui, root, create=create) | 464 self._repo = hg.repository(r.baseui, root, create=create) |
465 if self._repo.root != root: | 465 if self._repo.root != root: |
466 raise error.ProgrammingError( | 466 raise error.ProgrammingError( |
467 'failed to reject unsafe subrepo ' | 467 b'failed to reject unsafe subrepo ' |
468 'path: %s (expanded to %s)' % (root, self._repo.root) | 468 b'path: %s (expanded to %s)' % (root, self._repo.root) |
469 ) | 469 ) |
470 | 470 |
471 # Propagate the parent's --hidden option | 471 # Propagate the parent's --hidden option |
472 if r is r.unfiltered(): | 472 if r is r.unfiltered(): |
473 self._repo = self._repo.unfiltered() | 473 self._repo = self._repo.unfiltered() |
474 | 474 |
475 self.ui = self._repo.ui | 475 self.ui = self._repo.ui |
476 for s, k in [('ui', 'commitsubrepos')]: | 476 for s, k in [(b'ui', b'commitsubrepos')]: |
477 v = r.ui.config(s, k) | 477 v = r.ui.config(s, k) |
478 if v: | 478 if v: |
479 self.ui.setconfig(s, k, v, 'subrepo') | 479 self.ui.setconfig(s, k, v, b'subrepo') |
480 # internal config: ui._usedassubrepo | 480 # internal config: ui._usedassubrepo |
481 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo') | 481 self.ui.setconfig(b'ui', b'_usedassubrepo', b'True', b'subrepo') |
482 self._initrepo(r, state[0], create) | 482 self._initrepo(r, state[0], create) |
483 | 483 |
484 @annotatesubrepoerror | 484 @annotatesubrepoerror |
485 def addwebdirpath(self, serverpath, webconf): | 485 def addwebdirpath(self, serverpath, webconf): |
486 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf) | 486 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf) |
506 '''calculate a unique "store hash" | 506 '''calculate a unique "store hash" |
507 | 507 |
508 This method is used to to detect when there are changes that may | 508 This method is used to to detect when there are changes that may |
509 require a push to a given remote path.''' | 509 require a push to a given remote path.''' |
510 # sort the files that will be hashed in increasing (likely) file size | 510 # sort the files that will be hashed in increasing (likely) file size |
511 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i') | 511 filelist = (b'bookmarks', b'store/phaseroots', b'store/00changelog.i') |
512 yield '# %s\n' % _expandedabspath(remotepath) | 512 yield b'# %s\n' % _expandedabspath(remotepath) |
513 vfs = self._repo.vfs | 513 vfs = self._repo.vfs |
514 for relname in filelist: | 514 for relname in filelist: |
515 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest()) | 515 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest()) |
516 yield '%s = %s\n' % (relname, filehash) | 516 yield b'%s = %s\n' % (relname, filehash) |
517 | 517 |
518 @propertycache | 518 @propertycache |
519 def _cachestorehashvfs(self): | 519 def _cachestorehashvfs(self): |
520 return vfsmod.vfs(self._repo.vfs.join('cache/storehash')) | 520 return vfsmod.vfs(self._repo.vfs.join(b'cache/storehash')) |
521 | 521 |
522 def _readstorehashcache(self, remotepath): | 522 def _readstorehashcache(self, remotepath): |
523 '''read the store hash cache for a given remote repository''' | 523 '''read the store hash cache for a given remote repository''' |
524 cachefile = _getstorehashcachename(remotepath) | 524 cachefile = _getstorehashcachename(remotepath) |
525 return self._cachestorehashvfs.tryreadlines(cachefile, 'r') | 525 return self._cachestorehashvfs.tryreadlines(cachefile, b'r') |
526 | 526 |
527 def _cachestorehash(self, remotepath): | 527 def _cachestorehash(self, remotepath): |
528 '''cache the current store hash | 528 '''cache the current store hash |
529 | 529 |
530 Each remote repo requires its own store hash cache, because a subrepo | 530 Each remote repo requires its own store hash cache, because a subrepo |
532 ''' | 532 ''' |
533 cachefile = _getstorehashcachename(remotepath) | 533 cachefile = _getstorehashcachename(remotepath) |
534 with self._repo.lock(): | 534 with self._repo.lock(): |
535 storehash = list(self._calcstorehash(remotepath)) | 535 storehash = list(self._calcstorehash(remotepath)) |
536 vfs = self._cachestorehashvfs | 536 vfs = self._cachestorehashvfs |
537 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True) | 537 vfs.writelines(cachefile, storehash, mode=b'wb', notindexed=True) |
538 | 538 |
539 def _getctx(self): | 539 def _getctx(self): |
540 '''fetch the context for this subrepo revision, possibly a workingctx | 540 '''fetch the context for this subrepo revision, possibly a workingctx |
541 ''' | 541 ''' |
542 if self._ctx.rev() is None: | 542 if self._ctx.rev() is None: |
549 def _initrepo(self, parentrepo, source, create): | 549 def _initrepo(self, parentrepo, source, create): |
550 self._repo._subparent = parentrepo | 550 self._repo._subparent = parentrepo |
551 self._repo._subsource = source | 551 self._repo._subsource = source |
552 | 552 |
553 if create: | 553 if create: |
554 lines = ['[paths]\n'] | 554 lines = [b'[paths]\n'] |
555 | 555 |
556 def addpathconfig(key, value): | 556 def addpathconfig(key, value): |
557 if value: | 557 if value: |
558 lines.append('%s = %s\n' % (key, value)) | 558 lines.append(b'%s = %s\n' % (key, value)) |
559 self.ui.setconfig('paths', key, value, 'subrepo') | 559 self.ui.setconfig(b'paths', key, value, b'subrepo') |
560 | 560 |
561 defpath = _abssource(self._repo, abort=False) | 561 defpath = _abssource(self._repo, abort=False) |
562 defpushpath = _abssource(self._repo, True, abort=False) | 562 defpushpath = _abssource(self._repo, True, abort=False) |
563 addpathconfig('default', defpath) | 563 addpathconfig(b'default', defpath) |
564 if defpath != defpushpath: | 564 if defpath != defpushpath: |
565 addpathconfig('default-push', defpushpath) | 565 addpathconfig(b'default-push', defpushpath) |
566 | 566 |
567 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines))) | 567 self._repo.vfs.write(b'hgrc', util.tonativeeol(b''.join(lines))) |
568 | 568 |
569 @annotatesubrepoerror | 569 @annotatesubrepoerror |
570 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): | 570 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): |
571 return cmdutil.add( | 571 return cmdutil.add( |
572 ui, self._repo, match, prefix, uipathfn, explicitonly, **opts | 572 ui, self._repo, match, prefix, uipathfn, explicitonly, **opts |
576 def addremove(self, m, prefix, uipathfn, opts): | 576 def addremove(self, m, prefix, uipathfn, opts): |
577 # In the same way as sub directories are processed, once in a subrepo, | 577 # In the same way as sub directories are processed, once in a subrepo, |
578 # always entry any of its subrepos. Don't corrupt the options that will | 578 # always entry any of its subrepos. Don't corrupt the options that will |
579 # be used to process sibling subrepos however. | 579 # be used to process sibling subrepos however. |
580 opts = copy.copy(opts) | 580 opts = copy.copy(opts) |
581 opts['subrepos'] = True | 581 opts[b'subrepos'] = True |
582 return scmutil.addremove(self._repo, m, prefix, uipathfn, opts) | 582 return scmutil.addremove(self._repo, m, prefix, uipathfn, opts) |
583 | 583 |
584 @annotatesubrepoerror | 584 @annotatesubrepoerror |
585 def cat(self, match, fm, fntemplate, prefix, **opts): | 585 def cat(self, match, fm, fntemplate, prefix, **opts): |
586 rev = self._state[1] | 586 rev = self._state[1] |
596 ctx1 = self._repo[rev1] | 596 ctx1 = self._repo[rev1] |
597 ctx2 = self._repo[rev2] | 597 ctx2 = self._repo[rev2] |
598 return self._repo.status(ctx1, ctx2, **opts) | 598 return self._repo.status(ctx1, ctx2, **opts) |
599 except error.RepoLookupError as inst: | 599 except error.RepoLookupError as inst: |
600 self.ui.warn( | 600 self.ui.warn( |
601 _('warning: error "%s" in subrepository "%s"\n') | 601 _(b'warning: error "%s" in subrepository "%s"\n') |
602 % (inst, subrelpath(self)) | 602 % (inst, subrelpath(self)) |
603 ) | 603 ) |
604 return scmutil.status([], [], [], [], [], [], []) | 604 return scmutil.status([], [], [], [], [], [], []) |
605 | 605 |
606 @annotatesubrepoerror | 606 @annotatesubrepoerror |
622 listsubrepos=True, | 622 listsubrepos=True, |
623 **opts | 623 **opts |
624 ) | 624 ) |
625 except error.RepoLookupError as inst: | 625 except error.RepoLookupError as inst: |
626 self.ui.warn( | 626 self.ui.warn( |
627 _('warning: error "%s" in subrepository "%s"\n') | 627 _(b'warning: error "%s" in subrepository "%s"\n') |
628 % (inst, subrelpath(self)) | 628 % (inst, subrelpath(self)) |
629 ) | 629 ) |
630 | 630 |
631 @annotatesubrepoerror | 631 @annotatesubrepoerror |
632 def archive(self, archiver, prefix, match=None, decode=True): | 632 def archive(self, archiver, prefix, match=None, decode=True): |
633 self._get(self._state + ('hg',)) | 633 self._get(self._state + (b'hg',)) |
634 files = self.files() | 634 files = self.files() |
635 if match: | 635 if match: |
636 files = [f for f in files if match(f)] | 636 files = [f for f in files if match(f)] |
637 rev = self._state[1] | 637 rev = self._state[1] |
638 ctx = self._repo[rev] | 638 ctx = self._repo[rev] |
641 ) | 641 ) |
642 total = abstractsubrepo.archive(self, archiver, prefix, match) | 642 total = abstractsubrepo.archive(self, archiver, prefix, match) |
643 for subpath in ctx.substate: | 643 for subpath in ctx.substate: |
644 s = subrepo(ctx, subpath, True) | 644 s = subrepo(ctx, subpath, True) |
645 submatch = matchmod.subdirmatcher(subpath, match) | 645 submatch = matchmod.subdirmatcher(subpath, match) |
646 subprefix = prefix + subpath + '/' | 646 subprefix = prefix + subpath + b'/' |
647 total += s.archive(archiver, subprefix, submatch, decode) | 647 total += s.archive(archiver, subprefix, submatch, decode) |
648 return total | 648 return total |
649 | 649 |
650 @annotatesubrepoerror | 650 @annotatesubrepoerror |
651 def dirty(self, ignoreupdate=False, missing=False): | 651 def dirty(self, ignoreupdate=False, missing=False): |
652 r = self._state[1] | 652 r = self._state[1] |
653 if r == '' and not ignoreupdate: # no state recorded | 653 if r == b'' and not ignoreupdate: # no state recorded |
654 return True | 654 return True |
655 w = self._repo[None] | 655 w = self._repo[None] |
656 if r != w.p1().hex() and not ignoreupdate: | 656 if r != w.p1().hex() and not ignoreupdate: |
657 # different version checked out | 657 # different version checked out |
658 return True | 658 return True |
659 return w.dirty(missing=missing) # working directory changed | 659 return w.dirty(missing=missing) # working directory changed |
660 | 660 |
661 def basestate(self): | 661 def basestate(self): |
662 return self._repo['.'].hex() | 662 return self._repo[b'.'].hex() |
663 | 663 |
664 def checknested(self, path): | 664 def checknested(self, path): |
665 return self._repo._checknested(self._repo.wjoin(path)) | 665 return self._repo._checknested(self._repo.wjoin(path)) |
666 | 666 |
667 @annotatesubrepoerror | 667 @annotatesubrepoerror |
668 def commit(self, text, user, date): | 668 def commit(self, text, user, date): |
669 # don't bother committing in the subrepo if it's only been | 669 # don't bother committing in the subrepo if it's only been |
670 # updated | 670 # updated |
671 if not self.dirty(True): | 671 if not self.dirty(True): |
672 return self._repo['.'].hex() | 672 return self._repo[b'.'].hex() |
673 self.ui.debug("committing subrepo %s\n" % subrelpath(self)) | 673 self.ui.debug(b"committing subrepo %s\n" % subrelpath(self)) |
674 n = self._repo.commit(text, user, date) | 674 n = self._repo.commit(text, user, date) |
675 if not n: | 675 if not n: |
676 return self._repo['.'].hex() # different version checked out | 676 return self._repo[b'.'].hex() # different version checked out |
677 return node.hex(n) | 677 return node.hex(n) |
678 | 678 |
679 @annotatesubrepoerror | 679 @annotatesubrepoerror |
680 def phase(self, state): | 680 def phase(self, state): |
681 return self._repo[state or '.'].phase() | 681 return self._repo[state or b'.'].phase() |
682 | 682 |
683 @annotatesubrepoerror | 683 @annotatesubrepoerror |
684 def remove(self): | 684 def remove(self): |
685 # we can't fully delete the repository as it may contain | 685 # we can't fully delete the repository as it may contain |
686 # local-only history | 686 # local-only history |
687 self.ui.note(_('removing subrepo %s\n') % subrelpath(self)) | 687 self.ui.note(_(b'removing subrepo %s\n') % subrelpath(self)) |
688 hg.clean(self._repo, node.nullid, False) | 688 hg.clean(self._repo, node.nullid, False) |
689 | 689 |
690 def _get(self, state): | 690 def _get(self, state): |
691 source, revision, kind = state | 691 source, revision, kind = state |
692 parentrepo = self._repo._subparent | 692 parentrepo = self._repo._subparent |
711 # assemble the repos in a tree, so that can't be consistently done. | 711 # assemble the repos in a tree, so that can't be consistently done. |
712 # A simpler option is for the user to configure clone pooling, and | 712 # A simpler option is for the user to configure clone pooling, and |
713 # work with that. | 713 # work with that. |
714 if parentrepo.shared() and hg.islocal(srcurl): | 714 if parentrepo.shared() and hg.islocal(srcurl): |
715 self.ui.status( | 715 self.ui.status( |
716 _('sharing subrepo %s from %s\n') | 716 _(b'sharing subrepo %s from %s\n') |
717 % (subrelpath(self), srcurl) | 717 % (subrelpath(self), srcurl) |
718 ) | 718 ) |
719 shared = hg.share( | 719 shared = hg.share( |
720 self._repo._subparent.baseui, | 720 self._repo._subparent.baseui, |
721 getpeer(), | 721 getpeer(), |
726 self._repo = shared.local() | 726 self._repo = shared.local() |
727 else: | 727 else: |
728 # TODO: find a common place for this and this code in the | 728 # TODO: find a common place for this and this code in the |
729 # share.py wrap of the clone command. | 729 # share.py wrap of the clone command. |
730 if parentrepo.shared(): | 730 if parentrepo.shared(): |
731 pool = self.ui.config('share', 'pool') | 731 pool = self.ui.config(b'share', b'pool') |
732 if pool: | 732 if pool: |
733 pool = util.expandpath(pool) | 733 pool = util.expandpath(pool) |
734 | 734 |
735 shareopts = { | 735 shareopts = { |
736 'pool': pool, | 736 b'pool': pool, |
737 'mode': self.ui.config('share', 'poolnaming'), | 737 b'mode': self.ui.config(b'share', b'poolnaming'), |
738 } | 738 } |
739 else: | 739 else: |
740 shareopts = {} | 740 shareopts = {} |
741 | 741 |
742 self.ui.status( | 742 self.ui.status( |
743 _('cloning subrepo %s from %s\n') | 743 _(b'cloning subrepo %s from %s\n') |
744 % (subrelpath(self), util.hidepassword(srcurl)) | 744 % (subrelpath(self), util.hidepassword(srcurl)) |
745 ) | 745 ) |
746 other, cloned = hg.clone( | 746 other, cloned = hg.clone( |
747 self._repo._subparent.baseui, | 747 self._repo._subparent.baseui, |
748 {}, | 748 {}, |
754 self._repo = cloned.local() | 754 self._repo = cloned.local() |
755 self._initrepo(parentrepo, source, create=True) | 755 self._initrepo(parentrepo, source, create=True) |
756 self._cachestorehash(srcurl) | 756 self._cachestorehash(srcurl) |
757 else: | 757 else: |
758 self.ui.status( | 758 self.ui.status( |
759 _('pulling subrepo %s from %s\n') | 759 _(b'pulling subrepo %s from %s\n') |
760 % (subrelpath(self), util.hidepassword(srcurl)) | 760 % (subrelpath(self), util.hidepassword(srcurl)) |
761 ) | 761 ) |
762 cleansub = self.storeclean(srcurl) | 762 cleansub = self.storeclean(srcurl) |
763 exchange.pull(self._repo, getpeer()) | 763 exchange.pull(self._repo, getpeer()) |
764 if cleansub: | 764 if cleansub: |
769 @annotatesubrepoerror | 769 @annotatesubrepoerror |
770 def get(self, state, overwrite=False): | 770 def get(self, state, overwrite=False): |
771 inrepo = self._get(state) | 771 inrepo = self._get(state) |
772 source, revision, kind = state | 772 source, revision, kind = state |
773 repo = self._repo | 773 repo = self._repo |
774 repo.ui.debug("getting subrepo %s\n" % self._path) | 774 repo.ui.debug(b"getting subrepo %s\n" % self._path) |
775 if inrepo: | 775 if inrepo: |
776 urepo = repo.unfiltered() | 776 urepo = repo.unfiltered() |
777 ctx = urepo[revision] | 777 ctx = urepo[revision] |
778 if ctx.hidden(): | 778 if ctx.hidden(): |
779 urepo.ui.warn( | 779 urepo.ui.warn( |
780 _('revision %s in subrepository "%s" is hidden\n') | 780 _(b'revision %s in subrepository "%s" is hidden\n') |
781 % (revision[0:12], self._path) | 781 % (revision[0:12], self._path) |
782 ) | 782 ) |
783 repo = urepo | 783 repo = urepo |
784 hg.updaterepo(repo, revision, overwrite) | 784 hg.updaterepo(repo, revision, overwrite) |
785 | 785 |
786 @annotatesubrepoerror | 786 @annotatesubrepoerror |
787 def merge(self, state): | 787 def merge(self, state): |
788 self._get(state) | 788 self._get(state) |
789 cur = self._repo['.'] | 789 cur = self._repo[b'.'] |
790 dst = self._repo[state[1]] | 790 dst = self._repo[state[1]] |
791 anc = dst.ancestor(cur) | 791 anc = dst.ancestor(cur) |
792 | 792 |
793 def mergefunc(): | 793 def mergefunc(): |
794 if anc == cur and dst.branch() == cur.branch(): | 794 if anc == cur and dst.branch() == cur.branch(): |
795 self.ui.debug( | 795 self.ui.debug( |
796 'updating subrepository "%s"\n' % subrelpath(self) | 796 b'updating subrepository "%s"\n' % subrelpath(self) |
797 ) | 797 ) |
798 hg.update(self._repo, state[1]) | 798 hg.update(self._repo, state[1]) |
799 elif anc == dst: | 799 elif anc == dst: |
800 self.ui.debug( | 800 self.ui.debug( |
801 'skipping subrepository "%s"\n' % subrelpath(self) | 801 b'skipping subrepository "%s"\n' % subrelpath(self) |
802 ) | 802 ) |
803 else: | 803 else: |
804 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self)) | 804 self.ui.debug( |
805 b'merging subrepository "%s"\n' % subrelpath(self) | |
806 ) | |
805 hg.merge(self._repo, state[1], remind=False) | 807 hg.merge(self._repo, state[1], remind=False) |
806 | 808 |
807 wctx = self._repo[None] | 809 wctx = self._repo[None] |
808 if self.dirty(): | 810 if self.dirty(): |
809 if anc != dst: | 811 if anc != dst: |
814 else: | 816 else: |
815 mergefunc() | 817 mergefunc() |
816 | 818 |
817 @annotatesubrepoerror | 819 @annotatesubrepoerror |
818 def push(self, opts): | 820 def push(self, opts): |
819 force = opts.get('force') | 821 force = opts.get(b'force') |
820 newbranch = opts.get('new_branch') | 822 newbranch = opts.get(b'new_branch') |
821 ssh = opts.get('ssh') | 823 ssh = opts.get(b'ssh') |
822 | 824 |
823 # push subrepos depth-first for coherent ordering | 825 # push subrepos depth-first for coherent ordering |
824 c = self._repo['.'] | 826 c = self._repo[b'.'] |
825 subs = c.substate # only repos that are committed | 827 subs = c.substate # only repos that are committed |
826 for s in sorted(subs): | 828 for s in sorted(subs): |
827 if c.sub(s).push(opts) == 0: | 829 if c.sub(s).push(opts) == 0: |
828 return False | 830 return False |
829 | 831 |
830 dsturl = _abssource(self._repo, True) | 832 dsturl = _abssource(self._repo, True) |
831 if not force: | 833 if not force: |
832 if self.storeclean(dsturl): | 834 if self.storeclean(dsturl): |
833 self.ui.status( | 835 self.ui.status( |
834 _('no changes made to subrepo %s since last push to %s\n') | 836 _(b'no changes made to subrepo %s since last push to %s\n') |
835 % (subrelpath(self), util.hidepassword(dsturl)) | 837 % (subrelpath(self), util.hidepassword(dsturl)) |
836 ) | 838 ) |
837 return None | 839 return None |
838 self.ui.status( | 840 self.ui.status( |
839 _('pushing subrepo %s to %s\n') | 841 _(b'pushing subrepo %s to %s\n') |
840 % (subrelpath(self), util.hidepassword(dsturl)) | 842 % (subrelpath(self), util.hidepassword(dsturl)) |
841 ) | 843 ) |
842 other = hg.peer(self._repo, {'ssh': ssh}, dsturl) | 844 other = hg.peer(self._repo, {b'ssh': ssh}, dsturl) |
843 res = exchange.push(self._repo, other, force, newbranch=newbranch) | 845 res = exchange.push(self._repo, other, force, newbranch=newbranch) |
844 | 846 |
845 # the repo is now clean | 847 # the repo is now clean |
846 self._cachestorehash(dsturl) | 848 self._cachestorehash(dsturl) |
847 return res.cgresult | 849 return res.cgresult |
848 | 850 |
849 @annotatesubrepoerror | 851 @annotatesubrepoerror |
850 def outgoing(self, ui, dest, opts): | 852 def outgoing(self, ui, dest, opts): |
851 if 'rev' in opts or 'branch' in opts: | 853 if b'rev' in opts or b'branch' in opts: |
852 opts = copy.copy(opts) | 854 opts = copy.copy(opts) |
853 opts.pop('rev', None) | 855 opts.pop(b'rev', None) |
854 opts.pop('branch', None) | 856 opts.pop(b'branch', None) |
855 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts) | 857 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts) |
856 | 858 |
857 @annotatesubrepoerror | 859 @annotatesubrepoerror |
858 def incoming(self, ui, source, opts): | 860 def incoming(self, ui, source, opts): |
859 if 'rev' in opts or 'branch' in opts: | 861 if b'rev' in opts or b'branch' in opts: |
860 opts = copy.copy(opts) | 862 opts = copy.copy(opts) |
861 opts.pop('rev', None) | 863 opts.pop(b'rev', None) |
862 opts.pop('branch', None) | 864 opts.pop(b'branch', None) |
863 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts) | 865 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts) |
864 | 866 |
865 @annotatesubrepoerror | 867 @annotatesubrepoerror |
866 def files(self): | 868 def files(self): |
867 rev = self._state[1] | 869 rev = self._state[1] |
908 sm = sub.matchfileset(expr, badfn=badfn) | 910 sm = sub.matchfileset(expr, badfn=badfn) |
909 pm = matchmod.prefixdirmatcher(subpath, sm, badfn=badfn) | 911 pm = matchmod.prefixdirmatcher(subpath, sm, badfn=badfn) |
910 matchers.append(pm) | 912 matchers.append(pm) |
911 except error.LookupError: | 913 except error.LookupError: |
912 self.ui.status( | 914 self.ui.status( |
913 _("skipping missing subrepository: %s\n") | 915 _(b"skipping missing subrepository: %s\n") |
914 % self.wvfs.reljoin(reporelpath(self), subpath) | 916 % self.wvfs.reljoin(reporelpath(self), subpath) |
915 ) | 917 ) |
916 if len(matchers) == 1: | 918 if len(matchers) == 1: |
917 return matchers[0] | 919 return matchers[0] |
918 return matchmod.unionmatcher(matchers) | 920 return matchmod.unionmatcher(matchers) |
963 # reverting a subrepo is a 2 step process: | 965 # reverting a subrepo is a 2 step process: |
964 # 1. if the no_backup is not set, revert all modified | 966 # 1. if the no_backup is not set, revert all modified |
965 # files inside the subrepo | 967 # files inside the subrepo |
966 # 2. update the subrepo to the revision specified in | 968 # 2. update the subrepo to the revision specified in |
967 # the corresponding substate dictionary | 969 # the corresponding substate dictionary |
968 self.ui.status(_('reverting subrepo %s\n') % substate[0]) | 970 self.ui.status(_(b'reverting subrepo %s\n') % substate[0]) |
969 if not opts.get(r'no_backup'): | 971 if not opts.get(r'no_backup'): |
970 # Revert all files on the subrepo, creating backups | 972 # Revert all files on the subrepo, creating backups |
971 # Note that this will not recursively revert subrepos | 973 # Note that this will not recursively revert subrepos |
972 # We could do it if there was a set:subrepos() predicate | 974 # We could do it if there was a set:subrepos() predicate |
973 opts = opts.copy() | 975 opts = opts.copy() |
982 | 984 |
983 def filerevert(self, *pats, **opts): | 985 def filerevert(self, *pats, **opts): |
984 ctx = self._repo[opts[r'rev']] | 986 ctx = self._repo[opts[r'rev']] |
985 parents = self._repo.dirstate.parents() | 987 parents = self._repo.dirstate.parents() |
986 if opts.get(r'all'): | 988 if opts.get(r'all'): |
987 pats = ['set:modified()'] | 989 pats = [b'set:modified()'] |
988 else: | 990 else: |
989 pats = [] | 991 pats = [] |
990 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts) | 992 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts) |
991 | 993 |
992 def shortid(self, revid): | 994 def shortid(self, revid): |
1005 | 1007 |
1006 # Nothing prevents a user from sharing in a repo, and then making that a | 1008 # Nothing prevents a user from sharing in a repo, and then making that a |
1007 # subrepo. Alternately, the previous unshare attempt may have failed | 1009 # subrepo. Alternately, the previous unshare attempt may have failed |
1008 # part way through. So recurse whether or not this layer is shared. | 1010 # part way through. So recurse whether or not this layer is shared. |
1009 if self._repo.shared(): | 1011 if self._repo.shared(): |
1010 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath) | 1012 self.ui.status(_(b"unsharing subrepo '%s'\n") % self._relpath) |
1011 | 1013 |
1012 hg.unshare(self.ui, self._repo) | 1014 hg.unshare(self.ui, self._repo) |
1013 | 1015 |
1014 def verify(self): | 1016 def verify(self): |
1015 try: | 1017 try: |
1018 if ctx.hidden(): | 1020 if ctx.hidden(): |
1019 # Since hidden revisions aren't pushed/pulled, it seems worth an | 1021 # Since hidden revisions aren't pushed/pulled, it seems worth an |
1020 # explicit warning. | 1022 # explicit warning. |
1021 ui = self._repo.ui | 1023 ui = self._repo.ui |
1022 ui.warn( | 1024 ui.warn( |
1023 _("subrepo '%s' is hidden in revision %s\n") | 1025 _(b"subrepo '%s' is hidden in revision %s\n") |
1024 % (self._relpath, node.short(self._ctx.node())) | 1026 % (self._relpath, node.short(self._ctx.node())) |
1025 ) | 1027 ) |
1026 return 0 | 1028 return 0 |
1027 except error.RepoLookupError: | 1029 except error.RepoLookupError: |
1028 # A missing subrepo revision may be a case of needing to pull it, so | 1030 # A missing subrepo revision may be a case of needing to pull it, so |
1029 # don't treat this as an error. | 1031 # don't treat this as an error. |
1030 self._repo.ui.warn( | 1032 self._repo.ui.warn( |
1031 _("subrepo '%s' not found in revision %s\n") | 1033 _(b"subrepo '%s' not found in revision %s\n") |
1032 % (self._relpath, node.short(self._ctx.node())) | 1034 % (self._relpath, node.short(self._ctx.node())) |
1033 ) | 1035 ) |
1034 return 0 | 1036 return 0 |
1035 | 1037 |
1036 @propertycache | 1038 @propertycache |
1049 | 1051 |
1050 class svnsubrepo(abstractsubrepo): | 1052 class svnsubrepo(abstractsubrepo): |
1051 def __init__(self, ctx, path, state, allowcreate): | 1053 def __init__(self, ctx, path, state, allowcreate): |
1052 super(svnsubrepo, self).__init__(ctx, path) | 1054 super(svnsubrepo, self).__init__(ctx, path) |
1053 self._state = state | 1055 self._state = state |
1054 self._exe = procutil.findexe('svn') | 1056 self._exe = procutil.findexe(b'svn') |
1055 if not self._exe: | 1057 if not self._exe: |
1056 raise error.Abort( | 1058 raise error.Abort( |
1057 _("'svn' executable not found for subrepo '%s'") % self._path | 1059 _(b"'svn' executable not found for subrepo '%s'") % self._path |
1058 ) | 1060 ) |
1059 | 1061 |
1060 def _svncommand(self, commands, filename='', failok=False): | 1062 def _svncommand(self, commands, filename=b'', failok=False): |
1061 cmd = [self._exe] | 1063 cmd = [self._exe] |
1062 extrakw = {} | 1064 extrakw = {} |
1063 if not self.ui.interactive(): | 1065 if not self.ui.interactive(): |
1064 # Making stdin be a pipe should prevent svn from behaving | 1066 # Making stdin be a pipe should prevent svn from behaving |
1065 # interactively even if we can't pass --non-interactive. | 1067 # interactively even if we can't pass --non-interactive. |
1066 extrakw[r'stdin'] = subprocess.PIPE | 1068 extrakw[r'stdin'] = subprocess.PIPE |
1067 # Starting in svn 1.5 --non-interactive is a global flag | 1069 # Starting in svn 1.5 --non-interactive is a global flag |
1068 # instead of being per-command, but we need to support 1.4 so | 1070 # instead of being per-command, but we need to support 1.4 so |
1069 # we have to be intelligent about what commands take | 1071 # we have to be intelligent about what commands take |
1070 # --non-interactive. | 1072 # --non-interactive. |
1071 if commands[0] in ('update', 'checkout', 'commit'): | 1073 if commands[0] in (b'update', b'checkout', b'commit'): |
1072 cmd.append('--non-interactive') | 1074 cmd.append(b'--non-interactive') |
1073 cmd.extend(commands) | 1075 cmd.extend(commands) |
1074 if filename is not None: | 1076 if filename is not None: |
1075 path = self.wvfs.reljoin( | 1077 path = self.wvfs.reljoin( |
1076 self._ctx.repo().origroot, self._path, filename | 1078 self._ctx.repo().origroot, self._path, filename |
1077 ) | 1079 ) |
1078 cmd.append(path) | 1080 cmd.append(path) |
1079 env = dict(encoding.environ) | 1081 env = dict(encoding.environ) |
1080 # Avoid localized output, preserve current locale for everything else. | 1082 # Avoid localized output, preserve current locale for everything else. |
1081 lc_all = env.get('LC_ALL') | 1083 lc_all = env.get(b'LC_ALL') |
1082 if lc_all: | 1084 if lc_all: |
1083 env['LANG'] = lc_all | 1085 env[b'LANG'] = lc_all |
1084 del env['LC_ALL'] | 1086 del env[b'LC_ALL'] |
1085 env['LC_MESSAGES'] = 'C' | 1087 env[b'LC_MESSAGES'] = b'C' |
1086 p = subprocess.Popen( | 1088 p = subprocess.Popen( |
1087 pycompat.rapply(procutil.tonativestr, cmd), | 1089 pycompat.rapply(procutil.tonativestr, cmd), |
1088 bufsize=-1, | 1090 bufsize=-1, |
1089 close_fds=procutil.closefds, | 1091 close_fds=procutil.closefds, |
1090 stdout=subprocess.PIPE, | 1092 stdout=subprocess.PIPE, |
1095 stdout, stderr = map(util.fromnativeeol, p.communicate()) | 1097 stdout, stderr = map(util.fromnativeeol, p.communicate()) |
1096 stderr = stderr.strip() | 1098 stderr = stderr.strip() |
1097 if not failok: | 1099 if not failok: |
1098 if p.returncode: | 1100 if p.returncode: |
1099 raise error.Abort( | 1101 raise error.Abort( |
1100 stderr or 'exited with code %d' % p.returncode | 1102 stderr or b'exited with code %d' % p.returncode |
1101 ) | 1103 ) |
1102 if stderr: | 1104 if stderr: |
1103 self.ui.warn(stderr + '\n') | 1105 self.ui.warn(stderr + b'\n') |
1104 return stdout, stderr | 1106 return stdout, stderr |
1105 | 1107 |
1106 @propertycache | 1108 @propertycache |
1107 def _svnversion(self): | 1109 def _svnversion(self): |
1108 output, err = self._svncommand(['--version', '--quiet'], filename=None) | 1110 output, err = self._svncommand( |
1111 [b'--version', b'--quiet'], filename=None | |
1112 ) | |
1109 m = re.search(br'^(\d+)\.(\d+)', output) | 1113 m = re.search(br'^(\d+)\.(\d+)', output) |
1110 if not m: | 1114 if not m: |
1111 raise error.Abort(_('cannot retrieve svn tool version')) | 1115 raise error.Abort(_(b'cannot retrieve svn tool version')) |
1112 return (int(m.group(1)), int(m.group(2))) | 1116 return (int(m.group(1)), int(m.group(2))) |
1113 | 1117 |
1114 def _svnmissing(self): | 1118 def _svnmissing(self): |
1115 return not self.wvfs.exists('.svn') | 1119 return not self.wvfs.exists(b'.svn') |
1116 | 1120 |
1117 def _wcrevs(self): | 1121 def _wcrevs(self): |
1118 # Get the working directory revision as well as the last | 1122 # Get the working directory revision as well as the last |
1119 # commit revision so we can compare the subrepo state with | 1123 # commit revision so we can compare the subrepo state with |
1120 # both. We used to store the working directory one. | 1124 # both. We used to store the working directory one. |
1121 output, err = self._svncommand(['info', '--xml']) | 1125 output, err = self._svncommand([b'info', b'--xml']) |
1122 doc = xml.dom.minidom.parseString(output) | 1126 doc = xml.dom.minidom.parseString(output) |
1123 entries = doc.getElementsByTagName(r'entry') | 1127 entries = doc.getElementsByTagName(r'entry') |
1124 lastrev, rev = '0', '0' | 1128 lastrev, rev = b'0', b'0' |
1125 if entries: | 1129 if entries: |
1126 rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or '0' | 1130 rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or b'0' |
1127 commits = entries[0].getElementsByTagName(r'commit') | 1131 commits = entries[0].getElementsByTagName(r'commit') |
1128 if commits: | 1132 if commits: |
1129 lastrev = ( | 1133 lastrev = ( |
1130 pycompat.bytestr(commits[0].getAttribute(r'revision')) | 1134 pycompat.bytestr(commits[0].getAttribute(r'revision')) |
1131 or '0' | 1135 or b'0' |
1132 ) | 1136 ) |
1133 return (lastrev, rev) | 1137 return (lastrev, rev) |
1134 | 1138 |
1135 def _wcrev(self): | 1139 def _wcrev(self): |
1136 return self._wcrevs()[0] | 1140 return self._wcrevs()[0] |
1139 """Return (changes, extchanges, missing) where changes is True | 1143 """Return (changes, extchanges, missing) where changes is True |
1140 if the working directory was changed, extchanges is | 1144 if the working directory was changed, extchanges is |
1141 True if any of these changes concern an external entry and missing | 1145 True if any of these changes concern an external entry and missing |
1142 is True if any change is a missing entry. | 1146 is True if any change is a missing entry. |
1143 """ | 1147 """ |
1144 output, err = self._svncommand(['status', '--xml']) | 1148 output, err = self._svncommand([b'status', b'--xml']) |
1145 externals, changes, missing = [], [], [] | 1149 externals, changes, missing = [], [], [] |
1146 doc = xml.dom.minidom.parseString(output) | 1150 doc = xml.dom.minidom.parseString(output) |
1147 for e in doc.getElementsByTagName(r'entry'): | 1151 for e in doc.getElementsByTagName(r'entry'): |
1148 s = e.getElementsByTagName(r'wc-status') | 1152 s = e.getElementsByTagName(r'wc-status') |
1149 if not s: | 1153 if not s: |
1169 return bool(changes), False, bool(missing) | 1173 return bool(changes), False, bool(missing) |
1170 | 1174 |
1171 @annotatesubrepoerror | 1175 @annotatesubrepoerror |
1172 def dirty(self, ignoreupdate=False, missing=False): | 1176 def dirty(self, ignoreupdate=False, missing=False): |
1173 if self._svnmissing(): | 1177 if self._svnmissing(): |
1174 return self._state[1] != '' | 1178 return self._state[1] != b'' |
1175 wcchanged = self._wcchanged() | 1179 wcchanged = self._wcchanged() |
1176 changed = wcchanged[0] or (missing and wcchanged[2]) | 1180 changed = wcchanged[0] or (missing and wcchanged[2]) |
1177 if not changed: | 1181 if not changed: |
1178 if self._state[1] in self._wcrevs() or ignoreupdate: | 1182 if self._state[1] in self._wcrevs() or ignoreupdate: |
1179 return False | 1183 return False |
1185 # Last committed rev is not the same than rev. We would | 1189 # Last committed rev is not the same than rev. We would |
1186 # like to take lastrev but we do not know if the subrepo | 1190 # like to take lastrev but we do not know if the subrepo |
1187 # URL exists at lastrev. Test it and fallback to rev it | 1191 # URL exists at lastrev. Test it and fallback to rev it |
1188 # is not there. | 1192 # is not there. |
1189 try: | 1193 try: |
1190 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)]) | 1194 self._svncommand( |
1195 [b'list', b'%s@%s' % (self._state[0], lastrev)] | |
1196 ) | |
1191 return lastrev | 1197 return lastrev |
1192 except error.Abort: | 1198 except error.Abort: |
1193 pass | 1199 pass |
1194 return rev | 1200 return rev |
1195 | 1201 |
1199 changed, extchanged, missing = self._wcchanged() | 1205 changed, extchanged, missing = self._wcchanged() |
1200 if not changed: | 1206 if not changed: |
1201 return self.basestate() | 1207 return self.basestate() |
1202 if extchanged: | 1208 if extchanged: |
1203 # Do not try to commit externals | 1209 # Do not try to commit externals |
1204 raise error.Abort(_('cannot commit svn externals')) | 1210 raise error.Abort(_(b'cannot commit svn externals')) |
1205 if missing: | 1211 if missing: |
1206 # svn can commit with missing entries but aborting like hg | 1212 # svn can commit with missing entries but aborting like hg |
1207 # seems a better approach. | 1213 # seems a better approach. |
1208 raise error.Abort(_('cannot commit missing svn entries')) | 1214 raise error.Abort(_(b'cannot commit missing svn entries')) |
1209 commitinfo, err = self._svncommand(['commit', '-m', text]) | 1215 commitinfo, err = self._svncommand([b'commit', b'-m', text]) |
1210 self.ui.status(commitinfo) | 1216 self.ui.status(commitinfo) |
1211 newrev = re.search('Committed revision ([0-9]+).', commitinfo) | 1217 newrev = re.search(b'Committed revision ([0-9]+).', commitinfo) |
1212 if not newrev: | 1218 if not newrev: |
1213 if not commitinfo.strip(): | 1219 if not commitinfo.strip(): |
1214 # Sometimes, our definition of "changed" differs from | 1220 # Sometimes, our definition of "changed" differs from |
1215 # svn one. For instance, svn ignores missing files | 1221 # svn one. For instance, svn ignores missing files |
1216 # when committing. If there are only missing files, no | 1222 # when committing. If there are only missing files, no |
1217 # commit is made, no output and no error code. | 1223 # commit is made, no output and no error code. |
1218 raise error.Abort(_('failed to commit svn changes')) | 1224 raise error.Abort(_(b'failed to commit svn changes')) |
1219 raise error.Abort(commitinfo.splitlines()[-1]) | 1225 raise error.Abort(commitinfo.splitlines()[-1]) |
1220 newrev = newrev.groups()[0] | 1226 newrev = newrev.groups()[0] |
1221 self.ui.status(self._svncommand(['update', '-r', newrev])[0]) | 1227 self.ui.status(self._svncommand([b'update', b'-r', newrev])[0]) |
1222 return newrev | 1228 return newrev |
1223 | 1229 |
1224 @annotatesubrepoerror | 1230 @annotatesubrepoerror |
1225 def remove(self): | 1231 def remove(self): |
1226 if self.dirty(): | 1232 if self.dirty(): |
1227 self.ui.warn( | 1233 self.ui.warn( |
1228 _('not removing repo %s because ' 'it has changes.\n') | 1234 _(b'not removing repo %s because ' b'it has changes.\n') |
1229 % self._path | 1235 % self._path |
1230 ) | 1236 ) |
1231 return | 1237 return |
1232 self.ui.note(_('removing subrepo %s\n') % self._path) | 1238 self.ui.note(_(b'removing subrepo %s\n') % self._path) |
1233 | 1239 |
1234 self.wvfs.rmtree(forcibly=True) | 1240 self.wvfs.rmtree(forcibly=True) |
1235 try: | 1241 try: |
1236 pwvfs = self._ctx.repo().wvfs | 1242 pwvfs = self._ctx.repo().wvfs |
1237 pwvfs.removedirs(pwvfs.dirname(self._path)) | 1243 pwvfs.removedirs(pwvfs.dirname(self._path)) |
1239 pass | 1245 pass |
1240 | 1246 |
1241 @annotatesubrepoerror | 1247 @annotatesubrepoerror |
1242 def get(self, state, overwrite=False): | 1248 def get(self, state, overwrite=False): |
1243 if overwrite: | 1249 if overwrite: |
1244 self._svncommand(['revert', '--recursive']) | 1250 self._svncommand([b'revert', b'--recursive']) |
1245 args = ['checkout'] | 1251 args = [b'checkout'] |
1246 if self._svnversion >= (1, 5): | 1252 if self._svnversion >= (1, 5): |
1247 args.append('--force') | 1253 args.append(b'--force') |
1248 # The revision must be specified at the end of the URL to properly | 1254 # The revision must be specified at the end of the URL to properly |
1249 # update to a directory which has since been deleted and recreated. | 1255 # update to a directory which has since been deleted and recreated. |
1250 args.append('%s@%s' % (state[0], state[1])) | 1256 args.append(b'%s@%s' % (state[0], state[1])) |
1251 | 1257 |
1252 # SEC: check that the ssh url is safe | 1258 # SEC: check that the ssh url is safe |
1253 util.checksafessh(state[0]) | 1259 util.checksafessh(state[0]) |
1254 | 1260 |
1255 status, err = self._svncommand(args, failok=True) | 1261 status, err = self._svncommand(args, failok=True) |
1256 _sanitize(self.ui, self.wvfs, '.svn') | 1262 _sanitize(self.ui, self.wvfs, b'.svn') |
1257 if not re.search('Checked out revision [0-9]+.', status): | 1263 if not re.search(b'Checked out revision [0-9]+.', status): |
1258 if 'is already a working copy for a different URL' in err and ( | 1264 if b'is already a working copy for a different URL' in err and ( |
1259 self._wcchanged()[:2] == (False, False) | 1265 self._wcchanged()[:2] == (False, False) |
1260 ): | 1266 ): |
1261 # obstructed but clean working copy, so just blow it away. | 1267 # obstructed but clean working copy, so just blow it away. |
1262 self.remove() | 1268 self.remove() |
1263 self.get(state, overwrite=False) | 1269 self.get(state, overwrite=False) |
1279 # push is a no-op for SVN | 1285 # push is a no-op for SVN |
1280 return True | 1286 return True |
1281 | 1287 |
1282 @annotatesubrepoerror | 1288 @annotatesubrepoerror |
1283 def files(self): | 1289 def files(self): |
1284 output = self._svncommand(['list', '--recursive', '--xml'])[0] | 1290 output = self._svncommand([b'list', b'--recursive', b'--xml'])[0] |
1285 doc = xml.dom.minidom.parseString(output) | 1291 doc = xml.dom.minidom.parseString(output) |
1286 paths = [] | 1292 paths = [] |
1287 for e in doc.getElementsByTagName(r'entry'): | 1293 for e in doc.getElementsByTagName(r'entry'): |
1288 kind = pycompat.bytestr(e.getAttribute(r'kind')) | 1294 kind = pycompat.bytestr(e.getAttribute(r'kind')) |
1289 if kind != 'file': | 1295 if kind != b'file': |
1290 continue | 1296 continue |
1291 name = r''.join( | 1297 name = r''.join( |
1292 c.data | 1298 c.data |
1293 for c in e.getElementsByTagName(r'name')[0].childNodes | 1299 for c in e.getElementsByTagName(r'name')[0].childNodes |
1294 if c.nodeType == c.TEXT_NODE | 1300 if c.nodeType == c.TEXT_NODE |
1295 ) | 1301 ) |
1296 paths.append(name.encode('utf8')) | 1302 paths.append(name.encode('utf8')) |
1297 return paths | 1303 return paths |
1298 | 1304 |
1299 def filedata(self, name, decode): | 1305 def filedata(self, name, decode): |
1300 return self._svncommand(['cat'], name)[0] | 1306 return self._svncommand([b'cat'], name)[0] |
1301 | 1307 |
1302 | 1308 |
1303 class gitsubrepo(abstractsubrepo): | 1309 class gitsubrepo(abstractsubrepo): |
1304 def __init__(self, ctx, path, state, allowcreate): | 1310 def __init__(self, ctx, path, state, allowcreate): |
1305 super(gitsubrepo, self).__init__(ctx, path) | 1311 super(gitsubrepo, self).__init__(ctx, path) |
1308 self._subparent = ctx.repo() | 1314 self._subparent = ctx.repo() |
1309 self._ensuregit() | 1315 self._ensuregit() |
1310 | 1316 |
1311 def _ensuregit(self): | 1317 def _ensuregit(self): |
1312 try: | 1318 try: |
1313 self._gitexecutable = 'git' | 1319 self._gitexecutable = b'git' |
1314 out, err = self._gitnodir(['--version']) | 1320 out, err = self._gitnodir([b'--version']) |
1315 except OSError as e: | 1321 except OSError as e: |
1316 genericerror = _("error executing git for subrepo '%s': %s") | 1322 genericerror = _(b"error executing git for subrepo '%s': %s") |
1317 notfoundhint = _("check git is installed and in your PATH") | 1323 notfoundhint = _(b"check git is installed and in your PATH") |
1318 if e.errno != errno.ENOENT: | 1324 if e.errno != errno.ENOENT: |
1319 raise error.Abort( | 1325 raise error.Abort( |
1320 genericerror % (self._path, encoding.strtolocal(e.strerror)) | 1326 genericerror % (self._path, encoding.strtolocal(e.strerror)) |
1321 ) | 1327 ) |
1322 elif pycompat.iswindows: | 1328 elif pycompat.iswindows: |
1323 try: | 1329 try: |
1324 self._gitexecutable = 'git.cmd' | 1330 self._gitexecutable = b'git.cmd' |
1325 out, err = self._gitnodir(['--version']) | 1331 out, err = self._gitnodir([b'--version']) |
1326 except OSError as e2: | 1332 except OSError as e2: |
1327 if e2.errno == errno.ENOENT: | 1333 if e2.errno == errno.ENOENT: |
1328 raise error.Abort( | 1334 raise error.Abort( |
1329 _( | 1335 _( |
1330 "couldn't find 'git' or 'git.cmd'" | 1336 b"couldn't find 'git' or 'git.cmd'" |
1331 " for subrepo '%s'" | 1337 b" for subrepo '%s'" |
1332 ) | 1338 ) |
1333 % self._path, | 1339 % self._path, |
1334 hint=notfoundhint, | 1340 hint=notfoundhint, |
1335 ) | 1341 ) |
1336 else: | 1342 else: |
1338 genericerror | 1344 genericerror |
1339 % (self._path, encoding.strtolocal(e2.strerror)) | 1345 % (self._path, encoding.strtolocal(e2.strerror)) |
1340 ) | 1346 ) |
1341 else: | 1347 else: |
1342 raise error.Abort( | 1348 raise error.Abort( |
1343 _("couldn't find git for subrepo '%s'") % self._path, | 1349 _(b"couldn't find git for subrepo '%s'") % self._path, |
1344 hint=notfoundhint, | 1350 hint=notfoundhint, |
1345 ) | 1351 ) |
1346 versionstatus = self._checkversion(out) | 1352 versionstatus = self._checkversion(out) |
1347 if versionstatus == 'unknown': | 1353 if versionstatus == b'unknown': |
1348 self.ui.warn(_('cannot retrieve git version\n')) | 1354 self.ui.warn(_(b'cannot retrieve git version\n')) |
1349 elif versionstatus == 'abort': | 1355 elif versionstatus == b'abort': |
1350 raise error.Abort(_('git subrepo requires at least 1.6.0 or later')) | 1356 raise error.Abort( |
1351 elif versionstatus == 'warning': | 1357 _(b'git subrepo requires at least 1.6.0 or later') |
1352 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n')) | 1358 ) |
1359 elif versionstatus == b'warning': | |
1360 self.ui.warn(_(b'git subrepo requires at least 1.6.0 or later\n')) | |
1353 | 1361 |
1354 @staticmethod | 1362 @staticmethod |
1355 def _gitversion(out): | 1363 def _gitversion(out): |
1356 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out) | 1364 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out) |
1357 if m: | 1365 if m: |
1390 version = gitsubrepo._gitversion(out) | 1398 version = gitsubrepo._gitversion(out) |
1391 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases, | 1399 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases, |
1392 # despite the docstring comment. For now, error on 1.4.0, warn on | 1400 # despite the docstring comment. For now, error on 1.4.0, warn on |
1393 # 1.5.0 but attempt to continue. | 1401 # 1.5.0 but attempt to continue. |
1394 if version == -1: | 1402 if version == -1: |
1395 return 'unknown' | 1403 return b'unknown' |
1396 if version < (1, 5, 0): | 1404 if version < (1, 5, 0): |
1397 return 'abort' | 1405 return b'abort' |
1398 elif version < (1, 6, 0): | 1406 elif version < (1, 6, 0): |
1399 return 'warning' | 1407 return b'warning' |
1400 return 'ok' | 1408 return b'ok' |
1401 | 1409 |
1402 def _gitcommand(self, commands, env=None, stream=False): | 1410 def _gitcommand(self, commands, env=None, stream=False): |
1403 return self._gitdir(commands, env=env, stream=stream)[0] | 1411 return self._gitdir(commands, env=env, stream=stream)[0] |
1404 | 1412 |
1405 def _gitdir(self, commands, env=None, stream=False): | 1413 def _gitdir(self, commands, env=None, stream=False): |
1411 """Calls the git command | 1419 """Calls the git command |
1412 | 1420 |
1413 The methods tries to call the git command. versions prior to 1.6.0 | 1421 The methods tries to call the git command. versions prior to 1.6.0 |
1414 are not supported and very probably fail. | 1422 are not supported and very probably fail. |
1415 """ | 1423 """ |
1416 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands))) | 1424 self.ui.debug(b'%s: git %s\n' % (self._relpath, b' '.join(commands))) |
1417 if env is None: | 1425 if env is None: |
1418 env = encoding.environ.copy() | 1426 env = encoding.environ.copy() |
1419 # disable localization for Git output (issue5176) | 1427 # disable localization for Git output (issue5176) |
1420 env['LC_ALL'] = 'C' | 1428 env[b'LC_ALL'] = b'C' |
1421 # fix for Git CVE-2015-7545 | 1429 # fix for Git CVE-2015-7545 |
1422 if 'GIT_ALLOW_PROTOCOL' not in env: | 1430 if b'GIT_ALLOW_PROTOCOL' not in env: |
1423 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh' | 1431 env[b'GIT_ALLOW_PROTOCOL'] = b'file:git:http:https:ssh' |
1424 # unless ui.quiet is set, print git's stderr, | 1432 # unless ui.quiet is set, print git's stderr, |
1425 # which is mostly progress and useful info | 1433 # which is mostly progress and useful info |
1426 errpipe = None | 1434 errpipe = None |
1427 if self.ui.quiet: | 1435 if self.ui.quiet: |
1428 errpipe = open(os.devnull, 'w') | 1436 errpipe = open(os.devnull, b'w') |
1429 if self.ui._colormode and len(commands) and commands[0] == "diff": | 1437 if self.ui._colormode and len(commands) and commands[0] == b"diff": |
1430 # insert the argument in the front, | 1438 # insert the argument in the front, |
1431 # the end of git diff arguments is used for paths | 1439 # the end of git diff arguments is used for paths |
1432 commands.insert(1, '--color') | 1440 commands.insert(1, b'--color') |
1433 p = subprocess.Popen( | 1441 p = subprocess.Popen( |
1434 pycompat.rapply( | 1442 pycompat.rapply( |
1435 procutil.tonativestr, [self._gitexecutable] + commands | 1443 procutil.tonativestr, [self._gitexecutable] + commands |
1436 ), | 1444 ), |
1437 bufsize=-1, | 1445 bufsize=-1, |
1449 p.wait() | 1457 p.wait() |
1450 | 1458 |
1451 if p.returncode != 0 and p.returncode != 1: | 1459 if p.returncode != 0 and p.returncode != 1: |
1452 # there are certain error codes that are ok | 1460 # there are certain error codes that are ok |
1453 command = commands[0] | 1461 command = commands[0] |
1454 if command in ('cat-file', 'symbolic-ref'): | 1462 if command in (b'cat-file', b'symbolic-ref'): |
1455 return retdata, p.returncode | 1463 return retdata, p.returncode |
1456 # for all others, abort | 1464 # for all others, abort |
1457 raise error.Abort( | 1465 raise error.Abort( |
1458 _('git %s error %d in %s') | 1466 _(b'git %s error %d in %s') |
1459 % (command, p.returncode, self._relpath) | 1467 % (command, p.returncode, self._relpath) |
1460 ) | 1468 ) |
1461 | 1469 |
1462 return retdata, p.returncode | 1470 return retdata, p.returncode |
1463 | 1471 |
1464 def _gitmissing(self): | 1472 def _gitmissing(self): |
1465 return not self.wvfs.exists('.git') | 1473 return not self.wvfs.exists(b'.git') |
1466 | 1474 |
1467 def _gitstate(self): | 1475 def _gitstate(self): |
1468 return self._gitcommand(['rev-parse', 'HEAD']) | 1476 return self._gitcommand([b'rev-parse', b'HEAD']) |
1469 | 1477 |
1470 def _gitcurrentbranch(self): | 1478 def _gitcurrentbranch(self): |
1471 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet']) | 1479 current, err = self._gitdir([b'symbolic-ref', b'HEAD', b'--quiet']) |
1472 if err: | 1480 if err: |
1473 current = None | 1481 current = None |
1474 return current | 1482 return current |
1475 | 1483 |
1476 def _gitremote(self, remote): | 1484 def _gitremote(self, remote): |
1477 out = self._gitcommand(['remote', 'show', '-n', remote]) | 1485 out = self._gitcommand([b'remote', b'show', b'-n', remote]) |
1478 line = out.split('\n')[1] | 1486 line = out.split(b'\n')[1] |
1479 i = line.index('URL: ') + len('URL: ') | 1487 i = line.index(b'URL: ') + len(b'URL: ') |
1480 return line[i:] | 1488 return line[i:] |
1481 | 1489 |
1482 def _githavelocally(self, revision): | 1490 def _githavelocally(self, revision): |
1483 out, code = self._gitdir(['cat-file', '-e', revision]) | 1491 out, code = self._gitdir([b'cat-file', b'-e', revision]) |
1484 return code == 0 | 1492 return code == 0 |
1485 | 1493 |
1486 def _gitisancestor(self, r1, r2): | 1494 def _gitisancestor(self, r1, r2): |
1487 base = self._gitcommand(['merge-base', r1, r2]) | 1495 base = self._gitcommand([b'merge-base', r1, r2]) |
1488 return base == r1 | 1496 return base == r1 |
1489 | 1497 |
1490 def _gitisbare(self): | 1498 def _gitisbare(self): |
1491 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true' | 1499 return self._gitcommand([b'config', b'--bool', b'core.bare']) == b'true' |
1492 | 1500 |
1493 def _gitupdatestat(self): | 1501 def _gitupdatestat(self): |
1494 """This must be run before git diff-index. | 1502 """This must be run before git diff-index. |
1495 diff-index only looks at changes to file stat; | 1503 diff-index only looks at changes to file stat; |
1496 this command looks at file contents and updates the stat.""" | 1504 this command looks at file contents and updates the stat.""" |
1497 self._gitcommand(['update-index', '-q', '--refresh']) | 1505 self._gitcommand([b'update-index', b'-q', b'--refresh']) |
1498 | 1506 |
1499 def _gitbranchmap(self): | 1507 def _gitbranchmap(self): |
1500 '''returns 2 things: | 1508 '''returns 2 things: |
1501 a map from git branch to revision | 1509 a map from git branch to revision |
1502 a map from revision to branches''' | 1510 a map from revision to branches''' |
1503 branch2rev = {} | 1511 branch2rev = {} |
1504 rev2branch = {} | 1512 rev2branch = {} |
1505 | 1513 |
1506 out = self._gitcommand( | 1514 out = self._gitcommand( |
1507 ['for-each-ref', '--format', '%(objectname) %(refname)'] | 1515 [b'for-each-ref', b'--format', b'%(objectname) %(refname)'] |
1508 ) | 1516 ) |
1509 for line in out.split('\n'): | 1517 for line in out.split(b'\n'): |
1510 revision, ref = line.split(' ') | 1518 revision, ref = line.split(b' ') |
1511 if not ref.startswith('refs/heads/') and not ref.startswith( | 1519 if not ref.startswith(b'refs/heads/') and not ref.startswith( |
1512 'refs/remotes/' | 1520 b'refs/remotes/' |
1513 ): | 1521 ): |
1514 continue | 1522 continue |
1515 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'): | 1523 if ref.startswith(b'refs/remotes/') and ref.endswith(b'/HEAD'): |
1516 continue # ignore remote/HEAD redirects | 1524 continue # ignore remote/HEAD redirects |
1517 branch2rev[ref] = revision | 1525 branch2rev[ref] = revision |
1518 rev2branch.setdefault(revision, []).append(ref) | 1526 rev2branch.setdefault(revision, []).append(ref) |
1519 return branch2rev, rev2branch | 1527 return branch2rev, rev2branch |
1520 | 1528 |
1521 def _gittracking(self, branches): | 1529 def _gittracking(self, branches): |
1522 'return map of remote branch to local tracking branch' | 1530 b'return map of remote branch to local tracking branch' |
1523 # assumes no more than one local tracking branch for each remote | 1531 # assumes no more than one local tracking branch for each remote |
1524 tracking = {} | 1532 tracking = {} |
1525 for b in branches: | 1533 for b in branches: |
1526 if b.startswith('refs/remotes/'): | 1534 if b.startswith(b'refs/remotes/'): |
1527 continue | 1535 continue |
1528 bname = b.split('/', 2)[2] | 1536 bname = b.split(b'/', 2)[2] |
1529 remote = self._gitcommand(['config', 'branch.%s.remote' % bname]) | 1537 remote = self._gitcommand([b'config', b'branch.%s.remote' % bname]) |
1530 if remote: | 1538 if remote: |
1531 ref = self._gitcommand(['config', 'branch.%s.merge' % bname]) | 1539 ref = self._gitcommand([b'config', b'branch.%s.merge' % bname]) |
1532 tracking[ | 1540 tracking[ |
1533 'refs/remotes/%s/%s' % (remote, ref.split('/', 2)[2]) | 1541 b'refs/remotes/%s/%s' % (remote, ref.split(b'/', 2)[2]) |
1534 ] = b | 1542 ] = b |
1535 return tracking | 1543 return tracking |
1536 | 1544 |
1537 def _abssource(self, source): | 1545 def _abssource(self, source): |
1538 if '://' not in source: | 1546 if b'://' not in source: |
1539 # recognize the scp syntax as an absolute source | 1547 # recognize the scp syntax as an absolute source |
1540 colon = source.find(':') | 1548 colon = source.find(b':') |
1541 if colon != -1 and '/' not in source[:colon]: | 1549 if colon != -1 and b'/' not in source[:colon]: |
1542 return source | 1550 return source |
1543 self._subsource = source | 1551 self._subsource = source |
1544 return _abssource(self) | 1552 return _abssource(self) |
1545 | 1553 |
1546 def _fetch(self, source, revision): | 1554 def _fetch(self, source, revision): |
1548 # SEC: check for safe ssh url | 1556 # SEC: check for safe ssh url |
1549 util.checksafessh(source) | 1557 util.checksafessh(source) |
1550 | 1558 |
1551 source = self._abssource(source) | 1559 source = self._abssource(source) |
1552 self.ui.status( | 1560 self.ui.status( |
1553 _('cloning subrepo %s from %s\n') % (self._relpath, source) | 1561 _(b'cloning subrepo %s from %s\n') % (self._relpath, source) |
1554 ) | 1562 ) |
1555 self._gitnodir(['clone', source, self._abspath]) | 1563 self._gitnodir([b'clone', source, self._abspath]) |
1556 if self._githavelocally(revision): | 1564 if self._githavelocally(revision): |
1557 return | 1565 return |
1558 self.ui.status( | 1566 self.ui.status( |
1559 _('pulling subrepo %s from %s\n') | 1567 _(b'pulling subrepo %s from %s\n') |
1560 % (self._relpath, self._gitremote('origin')) | 1568 % (self._relpath, self._gitremote(b'origin')) |
1561 ) | 1569 ) |
1562 # try only origin: the originally cloned repo | 1570 # try only origin: the originally cloned repo |
1563 self._gitcommand(['fetch']) | 1571 self._gitcommand([b'fetch']) |
1564 if not self._githavelocally(revision): | 1572 if not self._githavelocally(revision): |
1565 raise error.Abort( | 1573 raise error.Abort( |
1566 _('revision %s does not exist in subrepository ' '"%s"\n') | 1574 _(b'revision %s does not exist in subrepository ' b'"%s"\n') |
1567 % (revision, self._relpath) | 1575 % (revision, self._relpath) |
1568 ) | 1576 ) |
1569 | 1577 |
1570 @annotatesubrepoerror | 1578 @annotatesubrepoerror |
1571 def dirty(self, ignoreupdate=False, missing=False): | 1579 def dirty(self, ignoreupdate=False, missing=False): |
1572 if self._gitmissing(): | 1580 if self._gitmissing(): |
1573 return self._state[1] != '' | 1581 return self._state[1] != b'' |
1574 if self._gitisbare(): | 1582 if self._gitisbare(): |
1575 return True | 1583 return True |
1576 if not ignoreupdate and self._state[1] != self._gitstate(): | 1584 if not ignoreupdate and self._state[1] != self._gitstate(): |
1577 # different version checked out | 1585 # different version checked out |
1578 return True | 1586 return True |
1579 # check for staged changes or modified files; ignore untracked files | 1587 # check for staged changes or modified files; ignore untracked files |
1580 self._gitupdatestat() | 1588 self._gitupdatestat() |
1581 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD']) | 1589 out, code = self._gitdir([b'diff-index', b'--quiet', b'HEAD']) |
1582 return code == 1 | 1590 return code == 1 |
1583 | 1591 |
1584 def basestate(self): | 1592 def basestate(self): |
1585 return self._gitstate() | 1593 return self._gitstate() |
1586 | 1594 |
1591 self.remove() | 1599 self.remove() |
1592 return | 1600 return |
1593 self._fetch(source, revision) | 1601 self._fetch(source, revision) |
1594 # if the repo was set to be bare, unbare it | 1602 # if the repo was set to be bare, unbare it |
1595 if self._gitisbare(): | 1603 if self._gitisbare(): |
1596 self._gitcommand(['config', 'core.bare', 'false']) | 1604 self._gitcommand([b'config', b'core.bare', b'false']) |
1597 if self._gitstate() == revision: | 1605 if self._gitstate() == revision: |
1598 self._gitcommand(['reset', '--hard', 'HEAD']) | 1606 self._gitcommand([b'reset', b'--hard', b'HEAD']) |
1599 return | 1607 return |
1600 elif self._gitstate() == revision: | 1608 elif self._gitstate() == revision: |
1601 if overwrite: | 1609 if overwrite: |
1602 # first reset the index to unmark new files for commit, because | 1610 # first reset the index to unmark new files for commit, because |
1603 # reset --hard will otherwise throw away files added for commit, | 1611 # reset --hard will otherwise throw away files added for commit, |
1604 # not just unmark them. | 1612 # not just unmark them. |
1605 self._gitcommand(['reset', 'HEAD']) | 1613 self._gitcommand([b'reset', b'HEAD']) |
1606 self._gitcommand(['reset', '--hard', 'HEAD']) | 1614 self._gitcommand([b'reset', b'--hard', b'HEAD']) |
1607 return | 1615 return |
1608 branch2rev, rev2branch = self._gitbranchmap() | 1616 branch2rev, rev2branch = self._gitbranchmap() |
1609 | 1617 |
1610 def checkout(args): | 1618 def checkout(args): |
1611 cmd = ['checkout'] | 1619 cmd = [b'checkout'] |
1612 if overwrite: | 1620 if overwrite: |
1613 # first reset the index to unmark new files for commit, because | 1621 # first reset the index to unmark new files for commit, because |
1614 # the -f option will otherwise throw away files added for | 1622 # the -f option will otherwise throw away files added for |
1615 # commit, not just unmark them. | 1623 # commit, not just unmark them. |
1616 self._gitcommand(['reset', 'HEAD']) | 1624 self._gitcommand([b'reset', b'HEAD']) |
1617 cmd.append('-f') | 1625 cmd.append(b'-f') |
1618 self._gitcommand(cmd + args) | 1626 self._gitcommand(cmd + args) |
1619 _sanitize(self.ui, self.wvfs, '.git') | 1627 _sanitize(self.ui, self.wvfs, b'.git') |
1620 | 1628 |
1621 def rawcheckout(): | 1629 def rawcheckout(): |
1622 # no branch to checkout, check it out with no branch | 1630 # no branch to checkout, check it out with no branch |
1623 self.ui.warn( | 1631 self.ui.warn( |
1624 _('checking out detached HEAD in ' 'subrepository "%s"\n') | 1632 _(b'checking out detached HEAD in ' b'subrepository "%s"\n') |
1625 % self._relpath | 1633 % self._relpath |
1626 ) | 1634 ) |
1627 self.ui.warn( | 1635 self.ui.warn( |
1628 _('check out a git branch if you intend ' 'to make changes\n') | 1636 _(b'check out a git branch if you intend ' b'to make changes\n') |
1629 ) | 1637 ) |
1630 checkout(['-q', revision]) | 1638 checkout([b'-q', revision]) |
1631 | 1639 |
1632 if revision not in rev2branch: | 1640 if revision not in rev2branch: |
1633 rawcheckout() | 1641 rawcheckout() |
1634 return | 1642 return |
1635 branches = rev2branch[revision] | 1643 branches = rev2branch[revision] |
1636 firstlocalbranch = None | 1644 firstlocalbranch = None |
1637 for b in branches: | 1645 for b in branches: |
1638 if b == 'refs/heads/master': | 1646 if b == b'refs/heads/master': |
1639 # master trumps all other branches | 1647 # master trumps all other branches |
1640 checkout(['refs/heads/master']) | 1648 checkout([b'refs/heads/master']) |
1641 return | 1649 return |
1642 if not firstlocalbranch and not b.startswith('refs/remotes/'): | 1650 if not firstlocalbranch and not b.startswith(b'refs/remotes/'): |
1643 firstlocalbranch = b | 1651 firstlocalbranch = b |
1644 if firstlocalbranch: | 1652 if firstlocalbranch: |
1645 checkout([firstlocalbranch]) | 1653 checkout([firstlocalbranch]) |
1646 return | 1654 return |
1647 | 1655 |
1654 remote = b | 1662 remote = b |
1655 break | 1663 break |
1656 | 1664 |
1657 if remote not in tracking: | 1665 if remote not in tracking: |
1658 # create a new local tracking branch | 1666 # create a new local tracking branch |
1659 local = remote.split('/', 3)[3] | 1667 local = remote.split(b'/', 3)[3] |
1660 checkout(['-b', local, remote]) | 1668 checkout([b'-b', local, remote]) |
1661 elif self._gitisancestor(branch2rev[tracking[remote]], remote): | 1669 elif self._gitisancestor(branch2rev[tracking[remote]], remote): |
1662 # When updating to a tracked remote branch, | 1670 # When updating to a tracked remote branch, |
1663 # if the local tracking branch is downstream of it, | 1671 # if the local tracking branch is downstream of it, |
1664 # a normal `git pull` would have performed a "fast-forward merge" | 1672 # a normal `git pull` would have performed a "fast-forward merge" |
1665 # which is equivalent to updating the local branch to the remote. | 1673 # which is equivalent to updating the local branch to the remote. |
1666 # Since we are only looking at branching at update, we need to | 1674 # Since we are only looking at branching at update, we need to |
1667 # detect this situation and perform this action lazily. | 1675 # detect this situation and perform this action lazily. |
1668 if tracking[remote] != self._gitcurrentbranch(): | 1676 if tracking[remote] != self._gitcurrentbranch(): |
1669 checkout([tracking[remote]]) | 1677 checkout([tracking[remote]]) |
1670 self._gitcommand(['merge', '--ff', remote]) | 1678 self._gitcommand([b'merge', b'--ff', remote]) |
1671 _sanitize(self.ui, self.wvfs, '.git') | 1679 _sanitize(self.ui, self.wvfs, b'.git') |
1672 else: | 1680 else: |
1673 # a real merge would be required, just checkout the revision | 1681 # a real merge would be required, just checkout the revision |
1674 rawcheckout() | 1682 rawcheckout() |
1675 | 1683 |
1676 @annotatesubrepoerror | 1684 @annotatesubrepoerror |
1677 def commit(self, text, user, date): | 1685 def commit(self, text, user, date): |
1678 if self._gitmissing(): | 1686 if self._gitmissing(): |
1679 raise error.Abort(_("subrepo %s is missing") % self._relpath) | 1687 raise error.Abort(_(b"subrepo %s is missing") % self._relpath) |
1680 cmd = ['commit', '-a', '-m', text] | 1688 cmd = [b'commit', b'-a', b'-m', text] |
1681 env = encoding.environ.copy() | 1689 env = encoding.environ.copy() |
1682 if user: | 1690 if user: |
1683 cmd += ['--author', user] | 1691 cmd += [b'--author', user] |
1684 if date: | 1692 if date: |
1685 # git's date parser silently ignores when seconds < 1e9 | 1693 # git's date parser silently ignores when seconds < 1e9 |
1686 # convert to ISO8601 | 1694 # convert to ISO8601 |
1687 env['GIT_AUTHOR_DATE'] = dateutil.datestr( | 1695 env[b'GIT_AUTHOR_DATE'] = dateutil.datestr( |
1688 date, '%Y-%m-%dT%H:%M:%S %1%2' | 1696 date, b'%Y-%m-%dT%H:%M:%S %1%2' |
1689 ) | 1697 ) |
1690 self._gitcommand(cmd, env=env) | 1698 self._gitcommand(cmd, env=env) |
1691 # make sure commit works otherwise HEAD might not exist under certain | 1699 # make sure commit works otherwise HEAD might not exist under certain |
1692 # circumstances | 1700 # circumstances |
1693 return self._gitstate() | 1701 return self._gitstate() |
1694 | 1702 |
1695 @annotatesubrepoerror | 1703 @annotatesubrepoerror |
1696 def merge(self, state): | 1704 def merge(self, state): |
1697 source, revision, kind = state | 1705 source, revision, kind = state |
1698 self._fetch(source, revision) | 1706 self._fetch(source, revision) |
1699 base = self._gitcommand(['merge-base', revision, self._state[1]]) | 1707 base = self._gitcommand([b'merge-base', revision, self._state[1]]) |
1700 self._gitupdatestat() | 1708 self._gitupdatestat() |
1701 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD']) | 1709 out, code = self._gitdir([b'diff-index', b'--quiet', b'HEAD']) |
1702 | 1710 |
1703 def mergefunc(): | 1711 def mergefunc(): |
1704 if base == revision: | 1712 if base == revision: |
1705 self.get(state) # fast forward merge | 1713 self.get(state) # fast forward merge |
1706 elif base != self._state[1]: | 1714 elif base != self._state[1]: |
1707 self._gitcommand(['merge', '--no-commit', revision]) | 1715 self._gitcommand([b'merge', b'--no-commit', revision]) |
1708 _sanitize(self.ui, self.wvfs, '.git') | 1716 _sanitize(self.ui, self.wvfs, b'.git') |
1709 | 1717 |
1710 if self.dirty(): | 1718 if self.dirty(): |
1711 if self._gitstate() != revision: | 1719 if self._gitstate() != revision: |
1712 dirty = self._gitstate() == self._state[1] or code != 0 | 1720 dirty = self._gitstate() == self._state[1] or code != 0 |
1713 if _updateprompt( | 1721 if _updateprompt( |
1717 else: | 1725 else: |
1718 mergefunc() | 1726 mergefunc() |
1719 | 1727 |
1720 @annotatesubrepoerror | 1728 @annotatesubrepoerror |
1721 def push(self, opts): | 1729 def push(self, opts): |
1722 force = opts.get('force') | 1730 force = opts.get(b'force') |
1723 | 1731 |
1724 if not self._state[1]: | 1732 if not self._state[1]: |
1725 return True | 1733 return True |
1726 if self._gitmissing(): | 1734 if self._gitmissing(): |
1727 raise error.Abort(_("subrepo %s is missing") % self._relpath) | 1735 raise error.Abort(_(b"subrepo %s is missing") % self._relpath) |
1728 # if a branch in origin contains the revision, nothing to do | 1736 # if a branch in origin contains the revision, nothing to do |
1729 branch2rev, rev2branch = self._gitbranchmap() | 1737 branch2rev, rev2branch = self._gitbranchmap() |
1730 if self._state[1] in rev2branch: | 1738 if self._state[1] in rev2branch: |
1731 for b in rev2branch[self._state[1]]: | 1739 for b in rev2branch[self._state[1]]: |
1732 if b.startswith('refs/remotes/origin/'): | 1740 if b.startswith(b'refs/remotes/origin/'): |
1733 return True | 1741 return True |
1734 for b, revision in branch2rev.iteritems(): | 1742 for b, revision in branch2rev.iteritems(): |
1735 if b.startswith('refs/remotes/origin/'): | 1743 if b.startswith(b'refs/remotes/origin/'): |
1736 if self._gitisancestor(self._state[1], revision): | 1744 if self._gitisancestor(self._state[1], revision): |
1737 return True | 1745 return True |
1738 # otherwise, try to push the currently checked out branch | 1746 # otherwise, try to push the currently checked out branch |
1739 cmd = ['push'] | 1747 cmd = [b'push'] |
1740 if force: | 1748 if force: |
1741 cmd.append('--force') | 1749 cmd.append(b'--force') |
1742 | 1750 |
1743 current = self._gitcurrentbranch() | 1751 current = self._gitcurrentbranch() |
1744 if current: | 1752 if current: |
1745 # determine if the current branch is even useful | 1753 # determine if the current branch is even useful |
1746 if not self._gitisancestor(self._state[1], current): | 1754 if not self._gitisancestor(self._state[1], current): |
1747 self.ui.warn( | 1755 self.ui.warn( |
1748 _( | 1756 _( |
1749 'unrelated git branch checked out ' | 1757 b'unrelated git branch checked out ' |
1750 'in subrepository "%s"\n' | 1758 b'in subrepository "%s"\n' |
1751 ) | 1759 ) |
1752 % self._relpath | 1760 % self._relpath |
1753 ) | 1761 ) |
1754 return False | 1762 return False |
1755 self.ui.status( | 1763 self.ui.status( |
1756 _('pushing branch %s of subrepository "%s"\n') | 1764 _(b'pushing branch %s of subrepository "%s"\n') |
1757 % (current.split('/', 2)[2], self._relpath) | 1765 % (current.split(b'/', 2)[2], self._relpath) |
1758 ) | 1766 ) |
1759 ret = self._gitdir(cmd + ['origin', current]) | 1767 ret = self._gitdir(cmd + [b'origin', current]) |
1760 return ret[1] == 0 | 1768 return ret[1] == 0 |
1761 else: | 1769 else: |
1762 self.ui.warn( | 1770 self.ui.warn( |
1763 _( | 1771 _( |
1764 'no branch checked out in subrepository "%s"\n' | 1772 b'no branch checked out in subrepository "%s"\n' |
1765 'cannot push revision %s\n' | 1773 b'cannot push revision %s\n' |
1766 ) | 1774 ) |
1767 % (self._relpath, self._state[1]) | 1775 % (self._relpath, self._state[1]) |
1768 ) | 1776 ) |
1769 return False | 1777 return False |
1770 | 1778 |
1787 rejected = [] | 1795 rejected = [] |
1788 | 1796 |
1789 files = [f for f in sorted(set(files)) if match(f)] | 1797 files = [f for f in sorted(set(files)) if match(f)] |
1790 for f in files: | 1798 for f in files: |
1791 exact = match.exact(f) | 1799 exact = match.exact(f) |
1792 command = ["add"] | 1800 command = [b"add"] |
1793 if exact: | 1801 if exact: |
1794 command.append("-f") # should be added, even if ignored | 1802 command.append(b"-f") # should be added, even if ignored |
1795 if ui.verbose or not exact: | 1803 if ui.verbose or not exact: |
1796 ui.status(_('adding %s\n') % uipathfn(f)) | 1804 ui.status(_(b'adding %s\n') % uipathfn(f)) |
1797 | 1805 |
1798 if f in tracked: # hg prints 'adding' even if already tracked | 1806 if f in tracked: # hg prints 'adding' even if already tracked |
1799 if exact: | 1807 if exact: |
1800 rejected.append(f) | 1808 rejected.append(f) |
1801 continue | 1809 continue |
1802 if not opts.get(r'dry_run'): | 1810 if not opts.get(r'dry_run'): |
1803 self._gitcommand(command + [f]) | 1811 self._gitcommand(command + [f]) |
1804 | 1812 |
1805 for f in rejected: | 1813 for f in rejected: |
1806 ui.warn(_("%s already tracked!\n") % uipathfn(f)) | 1814 ui.warn(_(b"%s already tracked!\n") % uipathfn(f)) |
1807 | 1815 |
1808 return rejected | 1816 return rejected |
1809 | 1817 |
1810 @annotatesubrepoerror | 1818 @annotatesubrepoerror |
1811 def remove(self): | 1819 def remove(self): |
1812 if self._gitmissing(): | 1820 if self._gitmissing(): |
1813 return | 1821 return |
1814 if self.dirty(): | 1822 if self.dirty(): |
1815 self.ui.warn( | 1823 self.ui.warn( |
1816 _('not removing repo %s because ' 'it has changes.\n') | 1824 _(b'not removing repo %s because ' b'it has changes.\n') |
1817 % self._relpath | 1825 % self._relpath |
1818 ) | 1826 ) |
1819 return | 1827 return |
1820 # we can't fully delete the repository as it may contain | 1828 # we can't fully delete the repository as it may contain |
1821 # local-only history | 1829 # local-only history |
1822 self.ui.note(_('removing subrepo %s\n') % self._relpath) | 1830 self.ui.note(_(b'removing subrepo %s\n') % self._relpath) |
1823 self._gitcommand(['config', 'core.bare', 'true']) | 1831 self._gitcommand([b'config', b'core.bare', b'true']) |
1824 for f, kind in self.wvfs.readdir(): | 1832 for f, kind in self.wvfs.readdir(): |
1825 if f == '.git': | 1833 if f == b'.git': |
1826 continue | 1834 continue |
1827 if kind == stat.S_IFDIR: | 1835 if kind == stat.S_IFDIR: |
1828 self.wvfs.rmtree(f) | 1836 self.wvfs.rmtree(f) |
1829 else: | 1837 else: |
1830 self.wvfs.unlink(f) | 1838 self.wvfs.unlink(f) |
1837 self._fetch(source, revision) | 1845 self._fetch(source, revision) |
1838 | 1846 |
1839 # Parse git's native archive command. | 1847 # Parse git's native archive command. |
1840 # This should be much faster than manually traversing the trees | 1848 # This should be much faster than manually traversing the trees |
1841 # and objects with many subprocess calls. | 1849 # and objects with many subprocess calls. |
1842 tarstream = self._gitcommand(['archive', revision], stream=True) | 1850 tarstream = self._gitcommand([b'archive', revision], stream=True) |
1843 tar = tarfile.open(fileobj=tarstream, mode=r'r|') | 1851 tar = tarfile.open(fileobj=tarstream, mode=r'r|') |
1844 relpath = subrelpath(self) | 1852 relpath = subrelpath(self) |
1845 progress = self.ui.makeprogress( | 1853 progress = self.ui.makeprogress( |
1846 _('archiving (%s)') % relpath, unit=_('files') | 1854 _(b'archiving (%s)') % relpath, unit=_(b'files') |
1847 ) | 1855 ) |
1848 progress.update(0) | 1856 progress.update(0) |
1849 for info in tar: | 1857 for info in tar: |
1850 if info.isdir(): | 1858 if info.isdir(): |
1851 continue | 1859 continue |
1871 if not match.files(): | 1879 if not match.files(): |
1872 return 1 | 1880 return 1 |
1873 | 1881 |
1874 # TODO: add support for non-plain formatter (see cmdutil.cat()) | 1882 # TODO: add support for non-plain formatter (see cmdutil.cat()) |
1875 for f in match.files(): | 1883 for f in match.files(): |
1876 output = self._gitcommand(["show", "%s:%s" % (rev, f)]) | 1884 output = self._gitcommand([b"show", b"%s:%s" % (rev, f)]) |
1877 fp = cmdutil.makefileobj( | 1885 fp = cmdutil.makefileobj( |
1878 self._ctx, fntemplate, pathname=self.wvfs.reljoin(prefix, f) | 1886 self._ctx, fntemplate, pathname=self.wvfs.reljoin(prefix, f) |
1879 ) | 1887 ) |
1880 fp.write(output) | 1888 fp.write(output) |
1881 fp.close() | 1889 fp.close() |
1888 # if the repo is missing, return no results | 1896 # if the repo is missing, return no results |
1889 return scmutil.status([], [], [], [], [], [], []) | 1897 return scmutil.status([], [], [], [], [], [], []) |
1890 modified, added, removed = [], [], [] | 1898 modified, added, removed = [], [], [] |
1891 self._gitupdatestat() | 1899 self._gitupdatestat() |
1892 if rev2: | 1900 if rev2: |
1893 command = ['diff-tree', '--no-renames', '-r', rev1, rev2] | 1901 command = [b'diff-tree', b'--no-renames', b'-r', rev1, rev2] |
1894 else: | 1902 else: |
1895 command = ['diff-index', '--no-renames', rev1] | 1903 command = [b'diff-index', b'--no-renames', rev1] |
1896 out = self._gitcommand(command) | 1904 out = self._gitcommand(command) |
1897 for line in out.split('\n'): | 1905 for line in out.split(b'\n'): |
1898 tab = line.find('\t') | 1906 tab = line.find(b'\t') |
1899 if tab == -1: | 1907 if tab == -1: |
1900 continue | 1908 continue |
1901 status, f = line[tab - 1 : tab], line[tab + 1 :] | 1909 status, f = line[tab - 1 : tab], line[tab + 1 :] |
1902 if status == 'M': | 1910 if status == b'M': |
1903 modified.append(f) | 1911 modified.append(f) |
1904 elif status == 'A': | 1912 elif status == b'A': |
1905 added.append(f) | 1913 added.append(f) |
1906 elif status == 'D': | 1914 elif status == b'D': |
1907 removed.append(f) | 1915 removed.append(f) |
1908 | 1916 |
1909 deleted, unknown, ignored, clean = [], [], [], [] | 1917 deleted, unknown, ignored, clean = [], [], [], [] |
1910 | 1918 |
1911 command = ['status', '--porcelain', '-z'] | 1919 command = [b'status', b'--porcelain', b'-z'] |
1912 if opts.get(r'unknown'): | 1920 if opts.get(r'unknown'): |
1913 command += ['--untracked-files=all'] | 1921 command += [b'--untracked-files=all'] |
1914 if opts.get(r'ignored'): | 1922 if opts.get(r'ignored'): |
1915 command += ['--ignored'] | 1923 command += [b'--ignored'] |
1916 out = self._gitcommand(command) | 1924 out = self._gitcommand(command) |
1917 | 1925 |
1918 changedfiles = set() | 1926 changedfiles = set() |
1919 changedfiles.update(modified) | 1927 changedfiles.update(modified) |
1920 changedfiles.update(added) | 1928 changedfiles.update(added) |
1921 changedfiles.update(removed) | 1929 changedfiles.update(removed) |
1922 for line in out.split('\0'): | 1930 for line in out.split(b'\0'): |
1923 if not line: | 1931 if not line: |
1924 continue | 1932 continue |
1925 st = line[0:2] | 1933 st = line[0:2] |
1926 # moves and copies show 2 files on one line | 1934 # moves and copies show 2 files on one line |
1927 if line.find('\0') >= 0: | 1935 if line.find(b'\0') >= 0: |
1928 filename1, filename2 = line[3:].split('\0') | 1936 filename1, filename2 = line[3:].split(b'\0') |
1929 else: | 1937 else: |
1930 filename1 = line[3:] | 1938 filename1 = line[3:] |
1931 filename2 = None | 1939 filename2 = None |
1932 | 1940 |
1933 changedfiles.add(filename1) | 1941 changedfiles.add(filename1) |
1934 if filename2: | 1942 if filename2: |
1935 changedfiles.add(filename2) | 1943 changedfiles.add(filename2) |
1936 | 1944 |
1937 if st == '??': | 1945 if st == b'??': |
1938 unknown.append(filename1) | 1946 unknown.append(filename1) |
1939 elif st == '!!': | 1947 elif st == b'!!': |
1940 ignored.append(filename1) | 1948 ignored.append(filename1) |
1941 | 1949 |
1942 if opts.get(r'clean'): | 1950 if opts.get(r'clean'): |
1943 out = self._gitcommand(['ls-files']) | 1951 out = self._gitcommand([b'ls-files']) |
1944 for f in out.split('\n'): | 1952 for f in out.split(b'\n'): |
1945 if not f in changedfiles: | 1953 if not f in changedfiles: |
1946 clean.append(f) | 1954 clean.append(f) |
1947 | 1955 |
1948 return scmutil.status( | 1956 return scmutil.status( |
1949 modified, added, removed, deleted, unknown, ignored, clean | 1957 modified, added, removed, deleted, unknown, ignored, clean |
1950 ) | 1958 ) |
1951 | 1959 |
1952 @annotatesubrepoerror | 1960 @annotatesubrepoerror |
1953 def diff(self, ui, diffopts, node2, match, prefix, **opts): | 1961 def diff(self, ui, diffopts, node2, match, prefix, **opts): |
1954 node1 = self._state[1] | 1962 node1 = self._state[1] |
1955 cmd = ['diff', '--no-renames'] | 1963 cmd = [b'diff', b'--no-renames'] |
1956 if opts[r'stat']: | 1964 if opts[r'stat']: |
1957 cmd.append('--stat') | 1965 cmd.append(b'--stat') |
1958 else: | 1966 else: |
1959 # for Git, this also implies '-p' | 1967 # for Git, this also implies '-p' |
1960 cmd.append('-U%d' % diffopts.context) | 1968 cmd.append(b'-U%d' % diffopts.context) |
1961 | 1969 |
1962 if diffopts.noprefix: | 1970 if diffopts.noprefix: |
1963 cmd.extend( | 1971 cmd.extend( |
1964 ['--src-prefix=%s/' % prefix, '--dst-prefix=%s/' % prefix] | 1972 [b'--src-prefix=%s/' % prefix, b'--dst-prefix=%s/' % prefix] |
1965 ) | 1973 ) |
1966 else: | 1974 else: |
1967 cmd.extend( | 1975 cmd.extend( |
1968 ['--src-prefix=a/%s/' % prefix, '--dst-prefix=b/%s/' % prefix] | 1976 [b'--src-prefix=a/%s/' % prefix, b'--dst-prefix=b/%s/' % prefix] |
1969 ) | 1977 ) |
1970 | 1978 |
1971 if diffopts.ignorews: | 1979 if diffopts.ignorews: |
1972 cmd.append('--ignore-all-space') | 1980 cmd.append(b'--ignore-all-space') |
1973 if diffopts.ignorewsamount: | 1981 if diffopts.ignorewsamount: |
1974 cmd.append('--ignore-space-change') | 1982 cmd.append(b'--ignore-space-change') |
1975 if ( | 1983 if ( |
1976 self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) | 1984 self._gitversion(self._gitcommand([b'--version'])) >= (1, 8, 4) |
1977 and diffopts.ignoreblanklines | 1985 and diffopts.ignoreblanklines |
1978 ): | 1986 ): |
1979 cmd.append('--ignore-blank-lines') | 1987 cmd.append(b'--ignore-blank-lines') |
1980 | 1988 |
1981 cmd.append(node1) | 1989 cmd.append(node1) |
1982 if node2: | 1990 if node2: |
1983 cmd.append(node2) | 1991 cmd.append(node2) |
1984 | 1992 |
1985 output = "" | 1993 output = b"" |
1986 if match.always(): | 1994 if match.always(): |
1987 output += self._gitcommand(cmd) + '\n' | 1995 output += self._gitcommand(cmd) + b'\n' |
1988 else: | 1996 else: |
1989 st = self.status(node2)[:3] | 1997 st = self.status(node2)[:3] |
1990 files = [f for sublist in st for f in sublist] | 1998 files = [f for sublist in st for f in sublist] |
1991 for f in files: | 1999 for f in files: |
1992 if match(f): | 2000 if match(f): |
1993 output += self._gitcommand(cmd + ['--', f]) + '\n' | 2001 output += self._gitcommand(cmd + [b'--', f]) + b'\n' |
1994 | 2002 |
1995 if output.strip(): | 2003 if output.strip(): |
1996 ui.write(output) | 2004 ui.write(output) |
1997 | 2005 |
1998 @annotatesubrepoerror | 2006 @annotatesubrepoerror |
1999 def revert(self, substate, *pats, **opts): | 2007 def revert(self, substate, *pats, **opts): |
2000 self.ui.status(_('reverting subrepo %s\n') % substate[0]) | 2008 self.ui.status(_(b'reverting subrepo %s\n') % substate[0]) |
2001 if not opts.get(r'no_backup'): | 2009 if not opts.get(r'no_backup'): |
2002 status = self.status(None) | 2010 status = self.status(None) |
2003 names = status.modified | 2011 names = status.modified |
2004 for name in names: | 2012 for name in names: |
2005 # backuppath() expects a path relative to the parent repo (the | 2013 # backuppath() expects a path relative to the parent repo (the |
2007 parentname = os.path.join(self._path, name) | 2015 parentname = os.path.join(self._path, name) |
2008 bakname = scmutil.backuppath( | 2016 bakname = scmutil.backuppath( |
2009 self.ui, self._subparent, parentname | 2017 self.ui, self._subparent, parentname |
2010 ) | 2018 ) |
2011 self.ui.note( | 2019 self.ui.note( |
2012 _('saving current version of %s as %s\n') | 2020 _(b'saving current version of %s as %s\n') |
2013 % (name, os.path.relpath(bakname)) | 2021 % (name, os.path.relpath(bakname)) |
2014 ) | 2022 ) |
2015 util.rename(self.wvfs.join(name), bakname) | 2023 util.rename(self.wvfs.join(name), bakname) |
2016 | 2024 |
2017 if not opts.get(r'dry_run'): | 2025 if not opts.get(r'dry_run'): |
2021 def shortid(self, revid): | 2029 def shortid(self, revid): |
2022 return revid[:7] | 2030 return revid[:7] |
2023 | 2031 |
2024 | 2032 |
2025 types = { | 2033 types = { |
2026 'hg': hgsubrepo, | 2034 b'hg': hgsubrepo, |
2027 'svn': svnsubrepo, | 2035 b'svn': svnsubrepo, |
2028 'git': gitsubrepo, | 2036 b'git': gitsubrepo, |
2029 } | 2037 } |