Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/subrepo.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | f6540aba8e3e |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
45 reporelpath = subrepoutil.reporelpath | 45 reporelpath = subrepoutil.reporelpath |
46 subrelpath = subrepoutil.subrelpath | 46 subrelpath = subrepoutil.subrelpath |
47 _abssource = subrepoutil._abssource | 47 _abssource = subrepoutil._abssource |
48 propertycache = util.propertycache | 48 propertycache = util.propertycache |
49 | 49 |
50 | |
50 def _expandedabspath(path): | 51 def _expandedabspath(path): |
51 ''' | 52 ''' |
52 get a path or url and if it is a path expand it and return an absolute path | 53 get a path or url and if it is a path expand it and return an absolute path |
53 ''' | 54 ''' |
54 expandedpath = util.urllocalpath(util.expandpath(path)) | 55 expandedpath = util.urllocalpath(util.expandpath(path)) |
55 u = util.url(expandedpath) | 56 u = util.url(expandedpath) |
56 if not u.scheme: | 57 if not u.scheme: |
57 path = util.normpath(os.path.abspath(u.path)) | 58 path = util.normpath(os.path.abspath(u.path)) |
58 return path | 59 return path |
59 | 60 |
61 | |
60 def _getstorehashcachename(remotepath): | 62 def _getstorehashcachename(remotepath): |
61 '''get a unique filename for the store hash cache of a remote repository''' | 63 '''get a unique filename for the store hash cache of a remote repository''' |
62 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12] | 64 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12] |
63 | 65 |
66 | |
64 class SubrepoAbort(error.Abort): | 67 class SubrepoAbort(error.Abort): |
65 """Exception class used to avoid handling a subrepo error more than once""" | 68 """Exception class used to avoid handling a subrepo error more than once""" |
69 | |
66 def __init__(self, *args, **kw): | 70 def __init__(self, *args, **kw): |
67 self.subrepo = kw.pop(r'subrepo', None) | 71 self.subrepo = kw.pop(r'subrepo', None) |
68 self.cause = kw.pop(r'cause', None) | 72 self.cause = kw.pop(r'cause', None) |
69 error.Abort.__init__(self, *args, **kw) | 73 error.Abort.__init__(self, *args, **kw) |
74 | |
70 | 75 |
71 def annotatesubrepoerror(func): | 76 def annotatesubrepoerror(func): |
72 def decoratedmethod(self, *args, **kargs): | 77 def decoratedmethod(self, *args, **kargs): |
73 try: | 78 try: |
74 res = func(self, *args, **kargs) | 79 res = func(self, *args, **kargs) |
75 except SubrepoAbort as ex: | 80 except SubrepoAbort as ex: |
76 # This exception has already been handled | 81 # This exception has already been handled |
77 raise ex | 82 raise ex |
78 except error.Abort as ex: | 83 except error.Abort as ex: |
79 subrepo = subrelpath(self) | 84 subrepo = subrelpath(self) |
80 errormsg = (stringutil.forcebytestr(ex) + ' ' | 85 errormsg = ( |
81 + _('(in subrepository "%s")') % subrepo) | 86 stringutil.forcebytestr(ex) |
87 + ' ' | |
88 + _('(in subrepository "%s")') % subrepo | |
89 ) | |
82 # avoid handling this exception by raising a SubrepoAbort exception | 90 # avoid handling this exception by raising a SubrepoAbort exception |
83 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo, | 91 raise SubrepoAbort( |
84 cause=sys.exc_info()) | 92 errormsg, hint=ex.hint, subrepo=subrepo, cause=sys.exc_info() |
93 ) | |
85 return res | 94 return res |
95 | |
86 return decoratedmethod | 96 return decoratedmethod |
97 | |
87 | 98 |
88 def _updateprompt(ui, sub, dirty, local, remote): | 99 def _updateprompt(ui, sub, dirty, local, remote): |
89 if dirty: | 100 if dirty: |
90 msg = (_(' subrepository sources for %s differ\n' | 101 msg = _( |
91 'you can use (l)ocal source (%s) or (r)emote source (%s).\n' | 102 ' subrepository sources for %s differ\n' |
92 'what do you want to do?' | 103 'you can use (l)ocal source (%s) or (r)emote source (%s).\n' |
93 '$$ &Local $$ &Remote') | 104 'what do you want to do?' |
94 % (subrelpath(sub), local, remote)) | 105 '$$ &Local $$ &Remote' |
106 ) % (subrelpath(sub), local, remote) | |
95 else: | 107 else: |
96 msg = (_(' subrepository sources for %s differ (in checked out ' | 108 msg = _( |
97 'version)\n' | 109 ' subrepository sources for %s differ (in checked out ' |
98 'you can use (l)ocal source (%s) or (r)emote source (%s).\n' | 110 'version)\n' |
99 'what do you want to do?' | 111 'you can use (l)ocal source (%s) or (r)emote source (%s).\n' |
100 '$$ &Local $$ &Remote') | 112 'what do you want to do?' |
101 % (subrelpath(sub), local, remote)) | 113 '$$ &Local $$ &Remote' |
114 ) % (subrelpath(sub), local, remote) | |
102 return ui.promptchoice(msg, 0) | 115 return ui.promptchoice(msg, 0) |
116 | |
103 | 117 |
104 def _sanitize(ui, vfs, ignore): | 118 def _sanitize(ui, vfs, ignore): |
105 for dirname, dirs, names in vfs.walk(): | 119 for dirname, dirs, names in vfs.walk(): |
106 for i, d in enumerate(dirs): | 120 for i, d in enumerate(dirs): |
107 if d.lower() == ignore: | 121 if d.lower() == ignore: |
109 break | 123 break |
110 if vfs.basename(dirname).lower() != '.hg': | 124 if vfs.basename(dirname).lower() != '.hg': |
111 continue | 125 continue |
112 for f in names: | 126 for f in names: |
113 if f.lower() == 'hgrc': | 127 if f.lower() == 'hgrc': |
114 ui.warn(_("warning: removing potentially hostile 'hgrc' " | 128 ui.warn( |
115 "in '%s'\n") % vfs.join(dirname)) | 129 _( |
130 "warning: removing potentially hostile 'hgrc' " | |
131 "in '%s'\n" | |
132 ) | |
133 % vfs.join(dirname) | |
134 ) | |
116 vfs.unlink(vfs.reljoin(dirname, f)) | 135 vfs.unlink(vfs.reljoin(dirname, f)) |
136 | |
117 | 137 |
118 def _auditsubrepopath(repo, path): | 138 def _auditsubrepopath(repo, path): |
119 # sanity check for potentially unsafe paths such as '~' and '$FOO' | 139 # sanity check for potentially unsafe paths such as '~' and '$FOO' |
120 if path.startswith('~') or '$' in path or util.expandpath(path) != path: | 140 if path.startswith('~') or '$' in path or util.expandpath(path) != path: |
121 raise error.Abort(_('subrepo path contains illegal component: %s') | 141 raise error.Abort( |
122 % path) | 142 _('subrepo path contains illegal component: %s') % path |
143 ) | |
123 # auditor doesn't check if the path itself is a symlink | 144 # auditor doesn't check if the path itself is a symlink |
124 pathutil.pathauditor(repo.root)(path) | 145 pathutil.pathauditor(repo.root)(path) |
125 if repo.wvfs.islink(path): | 146 if repo.wvfs.islink(path): |
126 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path) | 147 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path) |
148 | |
127 | 149 |
128 SUBREPO_ALLOWED_DEFAULTS = { | 150 SUBREPO_ALLOWED_DEFAULTS = { |
129 'hg': True, | 151 'hg': True, |
130 'git': False, | 152 'git': False, |
131 'svn': False, | 153 'svn': False, |
132 } | 154 } |
133 | 155 |
156 | |
134 def _checktype(ui, kind): | 157 def _checktype(ui, kind): |
135 # subrepos.allowed is a master kill switch. If disabled, subrepos are | 158 # subrepos.allowed is a master kill switch. If disabled, subrepos are |
136 # disabled period. | 159 # disabled period. |
137 if not ui.configbool('subrepos', 'allowed', True): | 160 if not ui.configbool('subrepos', 'allowed', True): |
138 raise error.Abort(_('subrepos not enabled'), | 161 raise error.Abort( |
139 hint=_("see 'hg help config.subrepos' for details")) | 162 _('subrepos not enabled'), |
163 hint=_("see 'hg help config.subrepos' for details"), | |
164 ) | |
140 | 165 |
141 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False) | 166 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False) |
142 if not ui.configbool('subrepos', '%s:allowed' % kind, default): | 167 if not ui.configbool('subrepos', '%s:allowed' % kind, default): |
143 raise error.Abort(_('%s subrepos not allowed') % kind, | 168 raise error.Abort( |
144 hint=_("see 'hg help config.subrepos' for details")) | 169 _('%s subrepos not allowed') % kind, |
170 hint=_("see 'hg help config.subrepos' for details"), | |
171 ) | |
145 | 172 |
146 if kind not in types: | 173 if kind not in types: |
147 raise error.Abort(_('unknown subrepo type %s') % kind) | 174 raise error.Abort(_('unknown subrepo type %s') % kind) |
175 | |
148 | 176 |
149 def subrepo(ctx, path, allowwdir=False, allowcreate=True): | 177 def subrepo(ctx, path, allowwdir=False, allowcreate=True): |
150 """return instance of the right subrepo class for subrepo in path""" | 178 """return instance of the right subrepo class for subrepo in path""" |
151 # subrepo inherently violates our import layering rules | 179 # subrepo inherently violates our import layering rules |
152 # because it wants to make repo objects from deep inside the stack | 180 # because it wants to make repo objects from deep inside the stack |
153 # so we manually delay the circular imports to not break | 181 # so we manually delay the circular imports to not break |
154 # scripts that don't use our demand-loading | 182 # scripts that don't use our demand-loading |
155 global hg | 183 global hg |
156 from . import hg as h | 184 from . import hg as h |
185 | |
157 hg = h | 186 hg = h |
158 | 187 |
159 repo = ctx.repo() | 188 repo = ctx.repo() |
160 _auditsubrepopath(repo, path) | 189 _auditsubrepopath(repo, path) |
161 state = ctx.substate[path] | 190 state = ctx.substate[path] |
162 _checktype(repo.ui, state[2]) | 191 _checktype(repo.ui, state[2]) |
163 if allowwdir: | 192 if allowwdir: |
164 state = (state[0], ctx.subrev(path), state[2]) | 193 state = (state[0], ctx.subrev(path), state[2]) |
165 return types[state[2]](ctx, path, state[:2], allowcreate) | 194 return types[state[2]](ctx, path, state[:2], allowcreate) |
195 | |
166 | 196 |
167 def nullsubrepo(ctx, path, pctx): | 197 def nullsubrepo(ctx, path, pctx): |
168 """return an empty subrepo in pctx for the extant subrepo in ctx""" | 198 """return an empty subrepo in pctx for the extant subrepo in ctx""" |
169 # subrepo inherently violates our import layering rules | 199 # subrepo inherently violates our import layering rules |
170 # because it wants to make repo objects from deep inside the stack | 200 # because it wants to make repo objects from deep inside the stack |
171 # so we manually delay the circular imports to not break | 201 # so we manually delay the circular imports to not break |
172 # scripts that don't use our demand-loading | 202 # scripts that don't use our demand-loading |
173 global hg | 203 global hg |
174 from . import hg as h | 204 from . import hg as h |
205 | |
175 hg = h | 206 hg = h |
176 | 207 |
177 repo = ctx.repo() | 208 repo = ctx.repo() |
178 _auditsubrepopath(repo, path) | 209 _auditsubrepopath(repo, path) |
179 state = ctx.substate[path] | 210 state = ctx.substate[path] |
181 subrev = '' | 212 subrev = '' |
182 if state[2] == 'hg': | 213 if state[2] == 'hg': |
183 subrev = "0" * 40 | 214 subrev = "0" * 40 |
184 return types[state[2]](pctx, path, (state[0], subrev), True) | 215 return types[state[2]](pctx, path, (state[0], subrev), True) |
185 | 216 |
217 | |
186 # subrepo classes need to implement the following abstract class: | 218 # subrepo classes need to implement the following abstract class: |
187 | 219 |
220 | |
188 class abstractsubrepo(object): | 221 class abstractsubrepo(object): |
189 | |
190 def __init__(self, ctx, path): | 222 def __init__(self, ctx, path): |
191 """Initialize abstractsubrepo part | 223 """Initialize abstractsubrepo part |
192 | 224 |
193 ``ctx`` is the context referring this subrepository in the | 225 ``ctx`` is the context referring this subrepository in the |
194 parent repository. | 226 parent repository. |
231 of exception. | 263 of exception. |
232 | 264 |
233 This returns None, otherwise. | 265 This returns None, otherwise. |
234 """ | 266 """ |
235 if self.dirty(ignoreupdate=ignoreupdate, missing=missing): | 267 if self.dirty(ignoreupdate=ignoreupdate, missing=missing): |
236 return _('uncommitted changes in subrepository "%s"' | 268 return _('uncommitted changes in subrepository "%s"') % subrelpath( |
237 ) % subrelpath(self) | 269 self |
270 ) | |
238 | 271 |
239 def bailifchanged(self, ignoreupdate=False, hint=None): | 272 def bailifchanged(self, ignoreupdate=False, hint=None): |
240 """raise Abort if subrepository is ``dirty()`` | 273 """raise Abort if subrepository is ``dirty()`` |
241 """ | 274 """ |
242 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate, | 275 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate, missing=True) |
243 missing=True) | |
244 if dirtyreason: | 276 if dirtyreason: |
245 raise error.Abort(dirtyreason, hint=hint) | 277 raise error.Abort(dirtyreason, hint=hint) |
246 | 278 |
247 def basestate(self): | 279 def basestate(self): |
248 """current working directory base state, disregarding .hgsubstate | 280 """current working directory base state, disregarding .hgsubstate |
336 files = [f for f in self.files() if match(f)] | 368 files = [f for f in self.files() if match(f)] |
337 else: | 369 else: |
338 files = self.files() | 370 files = self.files() |
339 total = len(files) | 371 total = len(files) |
340 relpath = subrelpath(self) | 372 relpath = subrelpath(self) |
341 progress = self.ui.makeprogress(_('archiving (%s)') % relpath, | 373 progress = self.ui.makeprogress( |
342 unit=_('files'), total=total) | 374 _('archiving (%s)') % relpath, unit=_('files'), total=total |
375 ) | |
343 progress.update(0) | 376 progress.update(0) |
344 for name in files: | 377 for name in files: |
345 flags = self.fileflags(name) | 378 flags = self.fileflags(name) |
346 mode = 'x' in flags and 0o755 or 0o644 | 379 mode = 'x' in flags and 0o755 or 0o644 |
347 symlink = 'l' in flags | 380 symlink = 'l' in flags |
348 archiver.addfile(prefix + name, mode, symlink, | 381 archiver.addfile( |
349 self.filedata(name, decode)) | 382 prefix + name, mode, symlink, self.filedata(name, decode) |
383 ) | |
350 progress.increment() | 384 progress.increment() |
351 progress.complete() | 385 progress.complete() |
352 return total | 386 return total |
353 | 387 |
354 def walk(self, match): | 388 def walk(self, match): |
358 ''' | 392 ''' |
359 | 393 |
360 def forget(self, match, prefix, uipathfn, dryrun, interactive): | 394 def forget(self, match, prefix, uipathfn, dryrun, interactive): |
361 return ([], []) | 395 return ([], []) |
362 | 396 |
363 def removefiles(self, matcher, prefix, uipathfn, after, force, subrepos, | 397 def removefiles( |
364 dryrun, warnings): | 398 self, |
399 matcher, | |
400 prefix, | |
401 uipathfn, | |
402 after, | |
403 force, | |
404 subrepos, | |
405 dryrun, | |
406 warnings, | |
407 ): | |
365 """remove the matched files from the subrepository and the filesystem, | 408 """remove the matched files from the subrepository and the filesystem, |
366 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 |
367 filesystem. Return 0 on success, 1 on any warning. | 410 filesystem. Return 0 on success, 1 on any warning. |
368 """ | 411 """ |
369 warnings.append(_("warning: removefiles not implemented (%s)") | 412 warnings.append( |
370 % self._path) | 413 _("warning: removefiles not implemented (%s)") % self._path |
414 ) | |
371 return 1 | 415 return 1 |
372 | 416 |
373 def revert(self, substate, *pats, **opts): | 417 def revert(self, substate, *pats, **opts): |
374 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') | 418 self.ui.warn( |
375 % (substate[0], substate[2])) | 419 _('%s: reverting %s subrepos is unsupported\n') |
420 % (substate[0], substate[2]) | |
421 ) | |
376 return [] | 422 return [] |
377 | 423 |
378 def shortid(self, revid): | 424 def shortid(self, revid): |
379 return revid | 425 return revid |
380 | 426 |
398 @propertycache | 444 @propertycache |
399 def _relpath(self): | 445 def _relpath(self): |
400 """return path to this subrepository as seen from outermost repository | 446 """return path to this subrepository as seen from outermost repository |
401 """ | 447 """ |
402 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path) | 448 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path) |
449 | |
403 | 450 |
404 class hgsubrepo(abstractsubrepo): | 451 class hgsubrepo(abstractsubrepo): |
405 def __init__(self, ctx, path, state, allowcreate): | 452 def __init__(self, ctx, path, state, allowcreate): |
406 super(hgsubrepo, self).__init__(ctx, path) | 453 super(hgsubrepo, self).__init__(ctx, path) |
407 self._state = state | 454 self._state = state |
409 root = r.wjoin(util.localpath(path)) | 456 root = r.wjoin(util.localpath(path)) |
410 create = allowcreate and not r.wvfs.exists('%s/.hg' % path) | 457 create = allowcreate and not r.wvfs.exists('%s/.hg' % path) |
411 # repository constructor does expand variables in path, which is | 458 # repository constructor does expand variables in path, which is |
412 # unsafe since subrepo path might come from untrusted source. | 459 # unsafe since subrepo path might come from untrusted source. |
413 if os.path.realpath(util.expandpath(root)) != root: | 460 if os.path.realpath(util.expandpath(root)) != root: |
414 raise error.Abort(_('subrepo path contains illegal component: %s') | 461 raise error.Abort( |
415 % path) | 462 _('subrepo path contains illegal component: %s') % path |
463 ) | |
416 self._repo = hg.repository(r.baseui, root, create=create) | 464 self._repo = hg.repository(r.baseui, root, create=create) |
417 if self._repo.root != root: | 465 if self._repo.root != root: |
418 raise error.ProgrammingError('failed to reject unsafe subrepo ' | 466 raise error.ProgrammingError( |
419 'path: %s (expanded to %s)' | 467 'failed to reject unsafe subrepo ' |
420 % (root, self._repo.root)) | 468 'path: %s (expanded to %s)' % (root, self._repo.root) |
469 ) | |
421 | 470 |
422 # Propagate the parent's --hidden option | 471 # Propagate the parent's --hidden option |
423 if r is r.unfiltered(): | 472 if r is r.unfiltered(): |
424 self._repo = self._repo.unfiltered() | 473 self._repo = self._repo.unfiltered() |
425 | 474 |
517 | 566 |
518 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines))) | 567 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines))) |
519 | 568 |
520 @annotatesubrepoerror | 569 @annotatesubrepoerror |
521 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): | 570 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): |
522 return cmdutil.add(ui, self._repo, match, prefix, uipathfn, | 571 return cmdutil.add( |
523 explicitonly, **opts) | 572 ui, self._repo, match, prefix, uipathfn, explicitonly, **opts |
573 ) | |
524 | 574 |
525 @annotatesubrepoerror | 575 @annotatesubrepoerror |
526 def addremove(self, m, prefix, uipathfn, opts): | 576 def addremove(self, m, prefix, uipathfn, opts): |
527 # 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, |
528 # 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 |
533 | 583 |
534 @annotatesubrepoerror | 584 @annotatesubrepoerror |
535 def cat(self, match, fm, fntemplate, prefix, **opts): | 585 def cat(self, match, fm, fntemplate, prefix, **opts): |
536 rev = self._state[1] | 586 rev = self._state[1] |
537 ctx = self._repo[rev] | 587 ctx = self._repo[rev] |
538 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate, | 588 return cmdutil.cat( |
539 prefix, **opts) | 589 self.ui, self._repo, ctx, match, fm, fntemplate, prefix, **opts |
590 ) | |
540 | 591 |
541 @annotatesubrepoerror | 592 @annotatesubrepoerror |
542 def status(self, rev2, **opts): | 593 def status(self, rev2, **opts): |
543 try: | 594 try: |
544 rev1 = self._state[1] | 595 rev1 = self._state[1] |
545 ctx1 = self._repo[rev1] | 596 ctx1 = self._repo[rev1] |
546 ctx2 = self._repo[rev2] | 597 ctx2 = self._repo[rev2] |
547 return self._repo.status(ctx1, ctx2, **opts) | 598 return self._repo.status(ctx1, ctx2, **opts) |
548 except error.RepoLookupError as inst: | 599 except error.RepoLookupError as inst: |
549 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n') | 600 self.ui.warn( |
550 % (inst, subrelpath(self))) | 601 _('warning: error "%s" in subrepository "%s"\n') |
602 % (inst, subrelpath(self)) | |
603 ) | |
551 return scmutil.status([], [], [], [], [], [], []) | 604 return scmutil.status([], [], [], [], [], [], []) |
552 | 605 |
553 @annotatesubrepoerror | 606 @annotatesubrepoerror |
554 def diff(self, ui, diffopts, node2, match, prefix, **opts): | 607 def diff(self, ui, diffopts, node2, match, prefix, **opts): |
555 try: | 608 try: |
556 node1 = node.bin(self._state[1]) | 609 node1 = node.bin(self._state[1]) |
557 # We currently expect node2 to come from substate and be | 610 # We currently expect node2 to come from substate and be |
558 # in hex format | 611 # in hex format |
559 if node2 is not None: | 612 if node2 is not None: |
560 node2 = node.bin(node2) | 613 node2 = node.bin(node2) |
561 logcmdutil.diffordiffstat(ui, self._repo, diffopts, node1, node2, | 614 logcmdutil.diffordiffstat( |
562 match, prefix=prefix, listsubrepos=True, | 615 ui, |
563 **opts) | 616 self._repo, |
617 diffopts, | |
618 node1, | |
619 node2, | |
620 match, | |
621 prefix=prefix, | |
622 listsubrepos=True, | |
623 **opts | |
624 ) | |
564 except error.RepoLookupError as inst: | 625 except error.RepoLookupError as inst: |
565 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n') | 626 self.ui.warn( |
566 % (inst, subrelpath(self))) | 627 _('warning: error "%s" in subrepository "%s"\n') |
628 % (inst, subrelpath(self)) | |
629 ) | |
567 | 630 |
568 @annotatesubrepoerror | 631 @annotatesubrepoerror |
569 def archive(self, archiver, prefix, match=None, decode=True): | 632 def archive(self, archiver, prefix, match=None, decode=True): |
570 self._get(self._state + ('hg',)) | 633 self._get(self._state + ('hg',)) |
571 files = self.files() | 634 files = self.files() |
572 if match: | 635 if match: |
573 files = [f for f in files if match(f)] | 636 files = [f for f in files if match(f)] |
574 rev = self._state[1] | 637 rev = self._state[1] |
575 ctx = self._repo[rev] | 638 ctx = self._repo[rev] |
576 scmutil.prefetchfiles(self._repo, [ctx.rev()], | 639 scmutil.prefetchfiles( |
577 scmutil.matchfiles(self._repo, files)) | 640 self._repo, [ctx.rev()], scmutil.matchfiles(self._repo, files) |
641 ) | |
578 total = abstractsubrepo.archive(self, archiver, prefix, match) | 642 total = abstractsubrepo.archive(self, archiver, prefix, match) |
579 for subpath in ctx.substate: | 643 for subpath in ctx.substate: |
580 s = subrepo(ctx, subpath, True) | 644 s = subrepo(ctx, subpath, True) |
581 submatch = matchmod.subdirmatcher(subpath, match) | 645 submatch = matchmod.subdirmatcher(subpath, match) |
582 subprefix = prefix + subpath + '/' | 646 subprefix = prefix + subpath + '/' |
583 total += s.archive(archiver, subprefix, submatch, | 647 total += s.archive(archiver, subprefix, submatch, decode) |
584 decode) | |
585 return total | 648 return total |
586 | 649 |
587 @annotatesubrepoerror | 650 @annotatesubrepoerror |
588 def dirty(self, ignoreupdate=False, missing=False): | 651 def dirty(self, ignoreupdate=False, missing=False): |
589 r = self._state[1] | 652 r = self._state[1] |
590 if r == '' and not ignoreupdate: # no state recorded | 653 if r == '' and not ignoreupdate: # no state recorded |
591 return True | 654 return True |
592 w = self._repo[None] | 655 w = self._repo[None] |
593 if r != w.p1().hex() and not ignoreupdate: | 656 if r != w.p1().hex() and not ignoreupdate: |
594 # different version checked out | 657 # different version checked out |
595 return True | 658 return True |
596 return w.dirty(missing=missing) # working directory changed | 659 return w.dirty(missing=missing) # working directory changed |
597 | 660 |
598 def basestate(self): | 661 def basestate(self): |
599 return self._repo['.'].hex() | 662 return self._repo['.'].hex() |
600 | 663 |
601 def checknested(self, path): | 664 def checknested(self, path): |
608 if not self.dirty(True): | 671 if not self.dirty(True): |
609 return self._repo['.'].hex() | 672 return self._repo['.'].hex() |
610 self.ui.debug("committing subrepo %s\n" % subrelpath(self)) | 673 self.ui.debug("committing subrepo %s\n" % subrelpath(self)) |
611 n = self._repo.commit(text, user, date) | 674 n = self._repo.commit(text, user, date) |
612 if not n: | 675 if not n: |
613 return self._repo['.'].hex() # different version checked out | 676 return self._repo['.'].hex() # different version checked out |
614 return node.hex(n) | 677 return node.hex(n) |
615 | 678 |
616 @annotatesubrepoerror | 679 @annotatesubrepoerror |
617 def phase(self, state): | 680 def phase(self, state): |
618 return self._repo[state or '.'].phase() | 681 return self._repo[state or '.'].phase() |
647 # relative to the parent's share source. But clone pooling doesn't | 710 # relative to the parent's share source. But clone pooling doesn't |
648 # 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. |
649 # 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 |
650 # work with that. | 713 # work with that. |
651 if parentrepo.shared() and hg.islocal(srcurl): | 714 if parentrepo.shared() and hg.islocal(srcurl): |
652 self.ui.status(_('sharing subrepo %s from %s\n') | 715 self.ui.status( |
653 % (subrelpath(self), srcurl)) | 716 _('sharing subrepo %s from %s\n') |
654 shared = hg.share(self._repo._subparent.baseui, | 717 % (subrelpath(self), srcurl) |
655 getpeer(), self._repo.root, | 718 ) |
656 update=False, bookmarks=False) | 719 shared = hg.share( |
720 self._repo._subparent.baseui, | |
721 getpeer(), | |
722 self._repo.root, | |
723 update=False, | |
724 bookmarks=False, | |
725 ) | |
657 self._repo = shared.local() | 726 self._repo = shared.local() |
658 else: | 727 else: |
659 # 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 |
660 # share.py wrap of the clone command. | 729 # share.py wrap of the clone command. |
661 if parentrepo.shared(): | 730 if parentrepo.shared(): |
668 'mode': self.ui.config('share', 'poolnaming'), | 737 'mode': self.ui.config('share', 'poolnaming'), |
669 } | 738 } |
670 else: | 739 else: |
671 shareopts = {} | 740 shareopts = {} |
672 | 741 |
673 self.ui.status(_('cloning subrepo %s from %s\n') | 742 self.ui.status( |
674 % (subrelpath(self), util.hidepassword(srcurl))) | 743 _('cloning subrepo %s from %s\n') |
675 other, cloned = hg.clone(self._repo._subparent.baseui, {}, | 744 % (subrelpath(self), util.hidepassword(srcurl)) |
676 getpeer(), self._repo.root, | 745 ) |
677 update=False, shareopts=shareopts) | 746 other, cloned = hg.clone( |
747 self._repo._subparent.baseui, | |
748 {}, | |
749 getpeer(), | |
750 self._repo.root, | |
751 update=False, | |
752 shareopts=shareopts, | |
753 ) | |
678 self._repo = cloned.local() | 754 self._repo = cloned.local() |
679 self._initrepo(parentrepo, source, create=True) | 755 self._initrepo(parentrepo, source, create=True) |
680 self._cachestorehash(srcurl) | 756 self._cachestorehash(srcurl) |
681 else: | 757 else: |
682 self.ui.status(_('pulling subrepo %s from %s\n') | 758 self.ui.status( |
683 % (subrelpath(self), util.hidepassword(srcurl))) | 759 _('pulling subrepo %s from %s\n') |
760 % (subrelpath(self), util.hidepassword(srcurl)) | |
761 ) | |
684 cleansub = self.storeclean(srcurl) | 762 cleansub = self.storeclean(srcurl) |
685 exchange.pull(self._repo, getpeer()) | 763 exchange.pull(self._repo, getpeer()) |
686 if cleansub: | 764 if cleansub: |
687 # keep the repo clean after pull | 765 # keep the repo clean after pull |
688 self._cachestorehash(srcurl) | 766 self._cachestorehash(srcurl) |
698 urepo = repo.unfiltered() | 776 urepo = repo.unfiltered() |
699 ctx = urepo[revision] | 777 ctx = urepo[revision] |
700 if ctx.hidden(): | 778 if ctx.hidden(): |
701 urepo.ui.warn( | 779 urepo.ui.warn( |
702 _('revision %s in subrepository "%s" is hidden\n') | 780 _('revision %s in subrepository "%s" is hidden\n') |
703 % (revision[0:12], self._path)) | 781 % (revision[0:12], self._path) |
782 ) | |
704 repo = urepo | 783 repo = urepo |
705 hg.updaterepo(repo, revision, overwrite) | 784 hg.updaterepo(repo, revision, overwrite) |
706 | 785 |
707 @annotatesubrepoerror | 786 @annotatesubrepoerror |
708 def merge(self, state): | 787 def merge(self, state): |
711 dst = self._repo[state[1]] | 790 dst = self._repo[state[1]] |
712 anc = dst.ancestor(cur) | 791 anc = dst.ancestor(cur) |
713 | 792 |
714 def mergefunc(): | 793 def mergefunc(): |
715 if anc == cur and dst.branch() == cur.branch(): | 794 if anc == cur and dst.branch() == cur.branch(): |
716 self.ui.debug('updating subrepository "%s"\n' | 795 self.ui.debug( |
717 % subrelpath(self)) | 796 'updating subrepository "%s"\n' % subrelpath(self) |
797 ) | |
718 hg.update(self._repo, state[1]) | 798 hg.update(self._repo, state[1]) |
719 elif anc == dst: | 799 elif anc == dst: |
720 self.ui.debug('skipping subrepository "%s"\n' | 800 self.ui.debug( |
721 % subrelpath(self)) | 801 'skipping subrepository "%s"\n' % subrelpath(self) |
802 ) | |
722 else: | 803 else: |
723 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self)) | 804 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self)) |
724 hg.merge(self._repo, state[1], remind=False) | 805 hg.merge(self._repo, state[1], remind=False) |
725 | 806 |
726 wctx = self._repo[None] | 807 wctx = self._repo[None] |
739 newbranch = opts.get('new_branch') | 820 newbranch = opts.get('new_branch') |
740 ssh = opts.get('ssh') | 821 ssh = opts.get('ssh') |
741 | 822 |
742 # push subrepos depth-first for coherent ordering | 823 # push subrepos depth-first for coherent ordering |
743 c = self._repo['.'] | 824 c = self._repo['.'] |
744 subs = c.substate # only repos that are committed | 825 subs = c.substate # only repos that are committed |
745 for s in sorted(subs): | 826 for s in sorted(subs): |
746 if c.sub(s).push(opts) == 0: | 827 if c.sub(s).push(opts) == 0: |
747 return False | 828 return False |
748 | 829 |
749 dsturl = _abssource(self._repo, True) | 830 dsturl = _abssource(self._repo, True) |
750 if not force: | 831 if not force: |
751 if self.storeclean(dsturl): | 832 if self.storeclean(dsturl): |
752 self.ui.status( | 833 self.ui.status( |
753 _('no changes made to subrepo %s since last push to %s\n') | 834 _('no changes made to subrepo %s since last push to %s\n') |
754 % (subrelpath(self), util.hidepassword(dsturl))) | 835 % (subrelpath(self), util.hidepassword(dsturl)) |
836 ) | |
755 return None | 837 return None |
756 self.ui.status(_('pushing subrepo %s to %s\n') % | 838 self.ui.status( |
757 (subrelpath(self), util.hidepassword(dsturl))) | 839 _('pushing subrepo %s to %s\n') |
840 % (subrelpath(self), util.hidepassword(dsturl)) | |
841 ) | |
758 other = hg.peer(self._repo, {'ssh': ssh}, dsturl) | 842 other = hg.peer(self._repo, {'ssh': ssh}, dsturl) |
759 res = exchange.push(self._repo, other, force, newbranch=newbranch) | 843 res = exchange.push(self._repo, other, force, newbranch=newbranch) |
760 | 844 |
761 # the repo is now clean | 845 # the repo is now clean |
762 self._cachestorehash(dsturl) | 846 self._cachestorehash(dsturl) |
823 try: | 907 try: |
824 sm = sub.matchfileset(expr, badfn=badfn) | 908 sm = sub.matchfileset(expr, badfn=badfn) |
825 pm = matchmod.prefixdirmatcher(subpath, sm, badfn=badfn) | 909 pm = matchmod.prefixdirmatcher(subpath, sm, badfn=badfn) |
826 matchers.append(pm) | 910 matchers.append(pm) |
827 except error.LookupError: | 911 except error.LookupError: |
828 self.ui.status(_("skipping missing subrepository: %s\n") | 912 self.ui.status( |
829 % self.wvfs.reljoin(reporelpath(self), subpath)) | 913 _("skipping missing subrepository: %s\n") |
914 % self.wvfs.reljoin(reporelpath(self), subpath) | |
915 ) | |
830 if len(matchers) == 1: | 916 if len(matchers) == 1: |
831 return matchers[0] | 917 return matchers[0] |
832 return matchmod.unionmatcher(matchers) | 918 return matchmod.unionmatcher(matchers) |
833 | 919 |
834 def walk(self, match): | 920 def walk(self, match): |
835 ctx = self._repo[None] | 921 ctx = self._repo[None] |
836 return ctx.walk(match) | 922 return ctx.walk(match) |
837 | 923 |
838 @annotatesubrepoerror | 924 @annotatesubrepoerror |
839 def forget(self, match, prefix, uipathfn, dryrun, interactive): | 925 def forget(self, match, prefix, uipathfn, dryrun, interactive): |
840 return cmdutil.forget(self.ui, self._repo, match, prefix, uipathfn, | 926 return cmdutil.forget( |
841 True, dryrun=dryrun, interactive=interactive) | 927 self.ui, |
842 | 928 self._repo, |
843 @annotatesubrepoerror | 929 match, |
844 def removefiles(self, matcher, prefix, uipathfn, after, force, subrepos, | 930 prefix, |
845 dryrun, warnings): | 931 uipathfn, |
846 return cmdutil.remove(self.ui, self._repo, matcher, prefix, uipathfn, | 932 True, |
847 after, force, subrepos, dryrun) | 933 dryrun=dryrun, |
934 interactive=interactive, | |
935 ) | |
936 | |
937 @annotatesubrepoerror | |
938 def removefiles( | |
939 self, | |
940 matcher, | |
941 prefix, | |
942 uipathfn, | |
943 after, | |
944 force, | |
945 subrepos, | |
946 dryrun, | |
947 warnings, | |
948 ): | |
949 return cmdutil.remove( | |
950 self.ui, | |
951 self._repo, | |
952 matcher, | |
953 prefix, | |
954 uipathfn, | |
955 after, | |
956 force, | |
957 subrepos, | |
958 dryrun, | |
959 ) | |
848 | 960 |
849 @annotatesubrepoerror | 961 @annotatesubrepoerror |
850 def revert(self, substate, *pats, **opts): | 962 def revert(self, substate, *pats, **opts): |
851 # reverting a subrepo is a 2 step process: | 963 # reverting a subrepo is a 2 step process: |
852 # 1. if the no_backup is not set, revert all modified | 964 # 1. if the no_backup is not set, revert all modified |
886 # because it wants to make repo objects from deep inside the stack | 998 # because it wants to make repo objects from deep inside the stack |
887 # so we manually delay the circular imports to not break | 999 # so we manually delay the circular imports to not break |
888 # scripts that don't use our demand-loading | 1000 # scripts that don't use our demand-loading |
889 global hg | 1001 global hg |
890 from . import hg as h | 1002 from . import hg as h |
1003 | |
891 hg = h | 1004 hg = h |
892 | 1005 |
893 # Nothing prevents a user from sharing in a repo, and then making that a | 1006 # Nothing prevents a user from sharing in a repo, and then making that a |
894 # subrepo. Alternately, the previous unshare attempt may have failed | 1007 # subrepo. Alternately, the previous unshare attempt may have failed |
895 # part way through. So recurse whether or not this layer is shared. | 1008 # part way through. So recurse whether or not this layer is shared. |
904 ctx = self._repo.unfiltered()[rev] | 1017 ctx = self._repo.unfiltered()[rev] |
905 if ctx.hidden(): | 1018 if ctx.hidden(): |
906 # Since hidden revisions aren't pushed/pulled, it seems worth an | 1019 # Since hidden revisions aren't pushed/pulled, it seems worth an |
907 # explicit warning. | 1020 # explicit warning. |
908 ui = self._repo.ui | 1021 ui = self._repo.ui |
909 ui.warn(_("subrepo '%s' is hidden in revision %s\n") % | 1022 ui.warn( |
910 (self._relpath, node.short(self._ctx.node()))) | 1023 _("subrepo '%s' is hidden in revision %s\n") |
1024 % (self._relpath, node.short(self._ctx.node())) | |
1025 ) | |
911 return 0 | 1026 return 0 |
912 except error.RepoLookupError: | 1027 except error.RepoLookupError: |
913 # A missing subrepo revision may be a case of needing to pull it, so | 1028 # A missing subrepo revision may be a case of needing to pull it, so |
914 # don't treat this as an error. | 1029 # don't treat this as an error. |
915 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") % | 1030 self._repo.ui.warn( |
916 (self._relpath, node.short(self._ctx.node()))) | 1031 _("subrepo '%s' not found in revision %s\n") |
1032 % (self._relpath, node.short(self._ctx.node())) | |
1033 ) | |
917 return 0 | 1034 return 0 |
918 | 1035 |
919 @propertycache | 1036 @propertycache |
920 def wvfs(self): | 1037 def wvfs(self): |
921 """return own wvfs for efficiency and consistency | 1038 """return own wvfs for efficiency and consistency |
927 """return path to this subrepository as seen from outermost repository | 1044 """return path to this subrepository as seen from outermost repository |
928 """ | 1045 """ |
929 # Keep consistent dir separators by avoiding vfs.join(self._path) | 1046 # Keep consistent dir separators by avoiding vfs.join(self._path) |
930 return reporelpath(self._repo) | 1047 return reporelpath(self._repo) |
931 | 1048 |
1049 | |
932 class svnsubrepo(abstractsubrepo): | 1050 class svnsubrepo(abstractsubrepo): |
933 def __init__(self, ctx, path, state, allowcreate): | 1051 def __init__(self, ctx, path, state, allowcreate): |
934 super(svnsubrepo, self).__init__(ctx, path) | 1052 super(svnsubrepo, self).__init__(ctx, path) |
935 self._state = state | 1053 self._state = state |
936 self._exe = procutil.findexe('svn') | 1054 self._exe = procutil.findexe('svn') |
937 if not self._exe: | 1055 if not self._exe: |
938 raise error.Abort(_("'svn' executable not found for subrepo '%s'") | 1056 raise error.Abort( |
939 % self._path) | 1057 _("'svn' executable not found for subrepo '%s'") % self._path |
1058 ) | |
940 | 1059 |
941 def _svncommand(self, commands, filename='', failok=False): | 1060 def _svncommand(self, commands, filename='', failok=False): |
942 cmd = [self._exe] | 1061 cmd = [self._exe] |
943 extrakw = {} | 1062 extrakw = {} |
944 if not self.ui.interactive(): | 1063 if not self.ui.interactive(): |
951 # --non-interactive. | 1070 # --non-interactive. |
952 if commands[0] in ('update', 'checkout', 'commit'): | 1071 if commands[0] in ('update', 'checkout', 'commit'): |
953 cmd.append('--non-interactive') | 1072 cmd.append('--non-interactive') |
954 cmd.extend(commands) | 1073 cmd.extend(commands) |
955 if filename is not None: | 1074 if filename is not None: |
956 path = self.wvfs.reljoin(self._ctx.repo().origroot, | 1075 path = self.wvfs.reljoin( |
957 self._path, filename) | 1076 self._ctx.repo().origroot, self._path, filename |
1077 ) | |
958 cmd.append(path) | 1078 cmd.append(path) |
959 env = dict(encoding.environ) | 1079 env = dict(encoding.environ) |
960 # Avoid localized output, preserve current locale for everything else. | 1080 # Avoid localized output, preserve current locale for everything else. |
961 lc_all = env.get('LC_ALL') | 1081 lc_all = env.get('LC_ALL') |
962 if lc_all: | 1082 if lc_all: |
963 env['LANG'] = lc_all | 1083 env['LANG'] = lc_all |
964 del env['LC_ALL'] | 1084 del env['LC_ALL'] |
965 env['LC_MESSAGES'] = 'C' | 1085 env['LC_MESSAGES'] = 'C' |
966 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd), | 1086 p = subprocess.Popen( |
967 bufsize=-1, close_fds=procutil.closefds, | 1087 pycompat.rapply(procutil.tonativestr, cmd), |
968 stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 1088 bufsize=-1, |
969 env=procutil.tonativeenv(env), **extrakw) | 1089 close_fds=procutil.closefds, |
1090 stdout=subprocess.PIPE, | |
1091 stderr=subprocess.PIPE, | |
1092 env=procutil.tonativeenv(env), | |
1093 **extrakw | |
1094 ) | |
970 stdout, stderr = map(util.fromnativeeol, p.communicate()) | 1095 stdout, stderr = map(util.fromnativeeol, p.communicate()) |
971 stderr = stderr.strip() | 1096 stderr = stderr.strip() |
972 if not failok: | 1097 if not failok: |
973 if p.returncode: | 1098 if p.returncode: |
974 raise error.Abort(stderr or 'exited with code %d' | 1099 raise error.Abort( |
975 % p.returncode) | 1100 stderr or 'exited with code %d' % p.returncode |
1101 ) | |
976 if stderr: | 1102 if stderr: |
977 self.ui.warn(stderr + '\n') | 1103 self.ui.warn(stderr + '\n') |
978 return stdout, stderr | 1104 return stdout, stderr |
979 | 1105 |
980 @propertycache | 1106 @propertycache |
998 lastrev, rev = '0', '0' | 1124 lastrev, rev = '0', '0' |
999 if entries: | 1125 if entries: |
1000 rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or '0' | 1126 rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or '0' |
1001 commits = entries[0].getElementsByTagName(r'commit') | 1127 commits = entries[0].getElementsByTagName(r'commit') |
1002 if commits: | 1128 if commits: |
1003 lastrev = pycompat.bytestr( | 1129 lastrev = ( |
1004 commits[0].getAttribute(r'revision')) or '0' | 1130 pycompat.bytestr(commits[0].getAttribute(r'revision')) |
1131 or '0' | |
1132 ) | |
1005 return (lastrev, rev) | 1133 return (lastrev, rev) |
1006 | 1134 |
1007 def _wcrev(self): | 1135 def _wcrev(self): |
1008 return self._wcrevs()[0] | 1136 return self._wcrevs()[0] |
1009 | 1137 |
1025 path = e.getAttribute(r'path').encode('utf8') | 1153 path = e.getAttribute(r'path').encode('utf8') |
1026 if item == r'external': | 1154 if item == r'external': |
1027 externals.append(path) | 1155 externals.append(path) |
1028 elif item == r'missing': | 1156 elif item == r'missing': |
1029 missing.append(path) | 1157 missing.append(path) |
1030 if (item not in (r'', r'normal', r'unversioned', r'external') | 1158 if item not in ( |
1031 or props not in (r'', r'none', r'normal')): | 1159 r'', |
1160 r'normal', | |
1161 r'unversioned', | |
1162 r'external', | |
1163 ) or props not in (r'', r'none', r'normal'): | |
1032 changes.append(path) | 1164 changes.append(path) |
1033 for path in changes: | 1165 for path in changes: |
1034 for ext in externals: | 1166 for ext in externals: |
1035 if path == ext or path.startswith(ext + pycompat.ossep): | 1167 if path == ext or path.startswith(ext + pycompat.ossep): |
1036 return True, True, bool(missing) | 1168 return True, True, bool(missing) |
1090 return newrev | 1222 return newrev |
1091 | 1223 |
1092 @annotatesubrepoerror | 1224 @annotatesubrepoerror |
1093 def remove(self): | 1225 def remove(self): |
1094 if self.dirty(): | 1226 if self.dirty(): |
1095 self.ui.warn(_('not removing repo %s because ' | 1227 self.ui.warn( |
1096 'it has changes.\n') % self._path) | 1228 _('not removing repo %s because ' 'it has changes.\n') |
1229 % self._path | |
1230 ) | |
1097 return | 1231 return |
1098 self.ui.note(_('removing subrepo %s\n') % self._path) | 1232 self.ui.note(_('removing subrepo %s\n') % self._path) |
1099 | 1233 |
1100 self.wvfs.rmtree(forcibly=True) | 1234 self.wvfs.rmtree(forcibly=True) |
1101 try: | 1235 try: |
1119 util.checksafessh(state[0]) | 1253 util.checksafessh(state[0]) |
1120 | 1254 |
1121 status, err = self._svncommand(args, failok=True) | 1255 status, err = self._svncommand(args, failok=True) |
1122 _sanitize(self.ui, self.wvfs, '.svn') | 1256 _sanitize(self.ui, self.wvfs, '.svn') |
1123 if not re.search('Checked out revision [0-9]+.', status): | 1257 if not re.search('Checked out revision [0-9]+.', status): |
1124 if ('is already a working copy for a different URL' in err | 1258 if 'is already a working copy for a different URL' in err and ( |
1125 and (self._wcchanged()[:2] == (False, False))): | 1259 self._wcchanged()[:2] == (False, False) |
1260 ): | |
1126 # obstructed but clean working copy, so just blow it away. | 1261 # obstructed but clean working copy, so just blow it away. |
1127 self.remove() | 1262 self.remove() |
1128 self.get(state, overwrite=False) | 1263 self.get(state, overwrite=False) |
1129 return | 1264 return |
1130 raise error.Abort((status or err).splitlines()[-1]) | 1265 raise error.Abort((status or err).splitlines()[-1]) |
1151 paths = [] | 1286 paths = [] |
1152 for e in doc.getElementsByTagName(r'entry'): | 1287 for e in doc.getElementsByTagName(r'entry'): |
1153 kind = pycompat.bytestr(e.getAttribute(r'kind')) | 1288 kind = pycompat.bytestr(e.getAttribute(r'kind')) |
1154 if kind != 'file': | 1289 if kind != 'file': |
1155 continue | 1290 continue |
1156 name = r''.join(c.data for c | 1291 name = r''.join( |
1157 in e.getElementsByTagName(r'name')[0].childNodes | 1292 c.data |
1158 if c.nodeType == c.TEXT_NODE) | 1293 for c in e.getElementsByTagName(r'name')[0].childNodes |
1294 if c.nodeType == c.TEXT_NODE | |
1295 ) | |
1159 paths.append(name.encode('utf8')) | 1296 paths.append(name.encode('utf8')) |
1160 return paths | 1297 return paths |
1161 | 1298 |
1162 def filedata(self, name, decode): | 1299 def filedata(self, name, decode): |
1163 return self._svncommand(['cat'], name)[0] | 1300 return self._svncommand(['cat'], name)[0] |
1177 out, err = self._gitnodir(['--version']) | 1314 out, err = self._gitnodir(['--version']) |
1178 except OSError as e: | 1315 except OSError as e: |
1179 genericerror = _("error executing git for subrepo '%s': %s") | 1316 genericerror = _("error executing git for subrepo '%s': %s") |
1180 notfoundhint = _("check git is installed and in your PATH") | 1317 notfoundhint = _("check git is installed and in your PATH") |
1181 if e.errno != errno.ENOENT: | 1318 if e.errno != errno.ENOENT: |
1182 raise error.Abort(genericerror % ( | 1319 raise error.Abort( |
1183 self._path, encoding.strtolocal(e.strerror))) | 1320 genericerror % (self._path, encoding.strtolocal(e.strerror)) |
1321 ) | |
1184 elif pycompat.iswindows: | 1322 elif pycompat.iswindows: |
1185 try: | 1323 try: |
1186 self._gitexecutable = 'git.cmd' | 1324 self._gitexecutable = 'git.cmd' |
1187 out, err = self._gitnodir(['--version']) | 1325 out, err = self._gitnodir(['--version']) |
1188 except OSError as e2: | 1326 except OSError as e2: |
1189 if e2.errno == errno.ENOENT: | 1327 if e2.errno == errno.ENOENT: |
1190 raise error.Abort(_("couldn't find 'git' or 'git.cmd'" | 1328 raise error.Abort( |
1191 " for subrepo '%s'") % self._path, | 1329 _( |
1192 hint=notfoundhint) | 1330 "couldn't find 'git' or 'git.cmd'" |
1331 " for subrepo '%s'" | |
1332 ) | |
1333 % self._path, | |
1334 hint=notfoundhint, | |
1335 ) | |
1193 else: | 1336 else: |
1194 raise error.Abort(genericerror % (self._path, | 1337 raise error.Abort( |
1195 encoding.strtolocal(e2.strerror))) | 1338 genericerror |
1339 % (self._path, encoding.strtolocal(e2.strerror)) | |
1340 ) | |
1196 else: | 1341 else: |
1197 raise error.Abort(_("couldn't find git for subrepo '%s'") | 1342 raise error.Abort( |
1198 % self._path, hint=notfoundhint) | 1343 _("couldn't find git for subrepo '%s'") % self._path, |
1344 hint=notfoundhint, | |
1345 ) | |
1199 versionstatus = self._checkversion(out) | 1346 versionstatus = self._checkversion(out) |
1200 if versionstatus == 'unknown': | 1347 if versionstatus == 'unknown': |
1201 self.ui.warn(_('cannot retrieve git version\n')) | 1348 self.ui.warn(_('cannot retrieve git version\n')) |
1202 elif versionstatus == 'abort': | 1349 elif versionstatus == 'abort': |
1203 raise error.Abort(_('git subrepo requires at least 1.6.0 or later')) | 1350 raise error.Abort(_('git subrepo requires at least 1.6.0 or later')) |
1254 | 1401 |
1255 def _gitcommand(self, commands, env=None, stream=False): | 1402 def _gitcommand(self, commands, env=None, stream=False): |
1256 return self._gitdir(commands, env=env, stream=stream)[0] | 1403 return self._gitdir(commands, env=env, stream=stream)[0] |
1257 | 1404 |
1258 def _gitdir(self, commands, env=None, stream=False): | 1405 def _gitdir(self, commands, env=None, stream=False): |
1259 return self._gitnodir(commands, env=env, stream=stream, | 1406 return self._gitnodir( |
1260 cwd=self._abspath) | 1407 commands, env=env, stream=stream, cwd=self._abspath |
1408 ) | |
1261 | 1409 |
1262 def _gitnodir(self, commands, env=None, stream=False, cwd=None): | 1410 def _gitnodir(self, commands, env=None, stream=False, cwd=None): |
1263 """Calls the git command | 1411 """Calls the git command |
1264 | 1412 |
1265 The methods tries to call the git command. versions prior to 1.6.0 | 1413 The methods tries to call the git command. versions prior to 1.6.0 |
1280 errpipe = open(os.devnull, 'w') | 1428 errpipe = open(os.devnull, 'w') |
1281 if self.ui._colormode and len(commands) and commands[0] == "diff": | 1429 if self.ui._colormode and len(commands) and commands[0] == "diff": |
1282 # insert the argument in the front, | 1430 # insert the argument in the front, |
1283 # the end of git diff arguments is used for paths | 1431 # the end of git diff arguments is used for paths |
1284 commands.insert(1, '--color') | 1432 commands.insert(1, '--color') |
1285 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, | 1433 p = subprocess.Popen( |
1286 [self._gitexecutable] + commands), | 1434 pycompat.rapply( |
1287 bufsize=-1, | 1435 procutil.tonativestr, [self._gitexecutable] + commands |
1288 cwd=pycompat.rapply(procutil.tonativestr, cwd), | 1436 ), |
1289 env=procutil.tonativeenv(env), | 1437 bufsize=-1, |
1290 close_fds=procutil.closefds, | 1438 cwd=pycompat.rapply(procutil.tonativestr, cwd), |
1291 stdout=subprocess.PIPE, stderr=errpipe) | 1439 env=procutil.tonativeenv(env), |
1440 close_fds=procutil.closefds, | |
1441 stdout=subprocess.PIPE, | |
1442 stderr=errpipe, | |
1443 ) | |
1292 if stream: | 1444 if stream: |
1293 return p.stdout, None | 1445 return p.stdout, None |
1294 | 1446 |
1295 retdata = p.stdout.read().strip() | 1447 retdata = p.stdout.read().strip() |
1296 # wait for the child to exit to avoid race condition. | 1448 # wait for the child to exit to avoid race condition. |
1300 # there are certain error codes that are ok | 1452 # there are certain error codes that are ok |
1301 command = commands[0] | 1453 command = commands[0] |
1302 if command in ('cat-file', 'symbolic-ref'): | 1454 if command in ('cat-file', 'symbolic-ref'): |
1303 return retdata, p.returncode | 1455 return retdata, p.returncode |
1304 # for all others, abort | 1456 # for all others, abort |
1305 raise error.Abort(_('git %s error %d in %s') % | 1457 raise error.Abort( |
1306 (command, p.returncode, self._relpath)) | 1458 _('git %s error %d in %s') |
1459 % (command, p.returncode, self._relpath) | |
1460 ) | |
1307 | 1461 |
1308 return retdata, p.returncode | 1462 return retdata, p.returncode |
1309 | 1463 |
1310 def _gitmissing(self): | 1464 def _gitmissing(self): |
1311 return not self.wvfs.exists('.git') | 1465 return not self.wvfs.exists('.git') |
1347 a map from git branch to revision | 1501 a map from git branch to revision |
1348 a map from revision to branches''' | 1502 a map from revision to branches''' |
1349 branch2rev = {} | 1503 branch2rev = {} |
1350 rev2branch = {} | 1504 rev2branch = {} |
1351 | 1505 |
1352 out = self._gitcommand(['for-each-ref', '--format', | 1506 out = self._gitcommand( |
1353 '%(objectname) %(refname)']) | 1507 ['for-each-ref', '--format', '%(objectname) %(refname)'] |
1508 ) | |
1354 for line in out.split('\n'): | 1509 for line in out.split('\n'): |
1355 revision, ref = line.split(' ') | 1510 revision, ref = line.split(' ') |
1356 if (not ref.startswith('refs/heads/') and | 1511 if not ref.startswith('refs/heads/') and not ref.startswith( |
1357 not ref.startswith('refs/remotes/')): | 1512 'refs/remotes/' |
1513 ): | |
1358 continue | 1514 continue |
1359 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'): | 1515 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'): |
1360 continue # ignore remote/HEAD redirects | 1516 continue # ignore remote/HEAD redirects |
1361 branch2rev[ref] = revision | 1517 branch2rev[ref] = revision |
1362 rev2branch.setdefault(revision, []).append(ref) | 1518 rev2branch.setdefault(revision, []).append(ref) |
1363 return branch2rev, rev2branch | 1519 return branch2rev, rev2branch |
1364 | 1520 |
1365 def _gittracking(self, branches): | 1521 def _gittracking(self, branches): |
1371 continue | 1527 continue |
1372 bname = b.split('/', 2)[2] | 1528 bname = b.split('/', 2)[2] |
1373 remote = self._gitcommand(['config', 'branch.%s.remote' % bname]) | 1529 remote = self._gitcommand(['config', 'branch.%s.remote' % bname]) |
1374 if remote: | 1530 if remote: |
1375 ref = self._gitcommand(['config', 'branch.%s.merge' % bname]) | 1531 ref = self._gitcommand(['config', 'branch.%s.merge' % bname]) |
1376 tracking['refs/remotes/%s/%s' % | 1532 tracking[ |
1377 (remote, ref.split('/', 2)[2])] = b | 1533 'refs/remotes/%s/%s' % (remote, ref.split('/', 2)[2]) |
1534 ] = b | |
1378 return tracking | 1535 return tracking |
1379 | 1536 |
1380 def _abssource(self, source): | 1537 def _abssource(self, source): |
1381 if '://' not in source: | 1538 if '://' not in source: |
1382 # recognize the scp syntax as an absolute source | 1539 # recognize the scp syntax as an absolute source |
1390 if self._gitmissing(): | 1547 if self._gitmissing(): |
1391 # SEC: check for safe ssh url | 1548 # SEC: check for safe ssh url |
1392 util.checksafessh(source) | 1549 util.checksafessh(source) |
1393 | 1550 |
1394 source = self._abssource(source) | 1551 source = self._abssource(source) |
1395 self.ui.status(_('cloning subrepo %s from %s\n') % | 1552 self.ui.status( |
1396 (self._relpath, source)) | 1553 _('cloning subrepo %s from %s\n') % (self._relpath, source) |
1554 ) | |
1397 self._gitnodir(['clone', source, self._abspath]) | 1555 self._gitnodir(['clone', source, self._abspath]) |
1398 if self._githavelocally(revision): | 1556 if self._githavelocally(revision): |
1399 return | 1557 return |
1400 self.ui.status(_('pulling subrepo %s from %s\n') % | 1558 self.ui.status( |
1401 (self._relpath, self._gitremote('origin'))) | 1559 _('pulling subrepo %s from %s\n') |
1560 % (self._relpath, self._gitremote('origin')) | |
1561 ) | |
1402 # try only origin: the originally cloned repo | 1562 # try only origin: the originally cloned repo |
1403 self._gitcommand(['fetch']) | 1563 self._gitcommand(['fetch']) |
1404 if not self._githavelocally(revision): | 1564 if not self._githavelocally(revision): |
1405 raise error.Abort(_('revision %s does not exist in subrepository ' | 1565 raise error.Abort( |
1406 '"%s"\n') % (revision, self._relpath)) | 1566 _('revision %s does not exist in subrepository ' '"%s"\n') |
1567 % (revision, self._relpath) | |
1568 ) | |
1407 | 1569 |
1408 @annotatesubrepoerror | 1570 @annotatesubrepoerror |
1409 def dirty(self, ignoreupdate=False, missing=False): | 1571 def dirty(self, ignoreupdate=False, missing=False): |
1410 if self._gitmissing(): | 1572 if self._gitmissing(): |
1411 return self._state[1] != '' | 1573 return self._state[1] != '' |
1456 self._gitcommand(cmd + args) | 1618 self._gitcommand(cmd + args) |
1457 _sanitize(self.ui, self.wvfs, '.git') | 1619 _sanitize(self.ui, self.wvfs, '.git') |
1458 | 1620 |
1459 def rawcheckout(): | 1621 def rawcheckout(): |
1460 # no branch to checkout, check it out with no branch | 1622 # no branch to checkout, check it out with no branch |
1461 self.ui.warn(_('checking out detached HEAD in ' | 1623 self.ui.warn( |
1462 'subrepository "%s"\n') % self._relpath) | 1624 _('checking out detached HEAD in ' 'subrepository "%s"\n') |
1463 self.ui.warn(_('check out a git branch if you intend ' | 1625 % self._relpath |
1464 'to make changes\n')) | 1626 ) |
1627 self.ui.warn( | |
1628 _('check out a git branch if you intend ' 'to make changes\n') | |
1629 ) | |
1465 checkout(['-q', revision]) | 1630 checkout(['-q', revision]) |
1466 | 1631 |
1467 if revision not in rev2branch: | 1632 if revision not in rev2branch: |
1468 rawcheckout() | 1633 rawcheckout() |
1469 return | 1634 return |
1517 if user: | 1682 if user: |
1518 cmd += ['--author', user] | 1683 cmd += ['--author', user] |
1519 if date: | 1684 if date: |
1520 # git's date parser silently ignores when seconds < 1e9 | 1685 # git's date parser silently ignores when seconds < 1e9 |
1521 # convert to ISO8601 | 1686 # convert to ISO8601 |
1522 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date, | 1687 env['GIT_AUTHOR_DATE'] = dateutil.datestr( |
1523 '%Y-%m-%dT%H:%M:%S %1%2') | 1688 date, '%Y-%m-%dT%H:%M:%S %1%2' |
1689 ) | |
1524 self._gitcommand(cmd, env=env) | 1690 self._gitcommand(cmd, env=env) |
1525 # make sure commit works otherwise HEAD might not exist under certain | 1691 # make sure commit works otherwise HEAD might not exist under certain |
1526 # circumstances | 1692 # circumstances |
1527 return self._gitstate() | 1693 return self._gitstate() |
1528 | 1694 |
1534 self._gitupdatestat() | 1700 self._gitupdatestat() |
1535 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD']) | 1701 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD']) |
1536 | 1702 |
1537 def mergefunc(): | 1703 def mergefunc(): |
1538 if base == revision: | 1704 if base == revision: |
1539 self.get(state) # fast forward merge | 1705 self.get(state) # fast forward merge |
1540 elif base != self._state[1]: | 1706 elif base != self._state[1]: |
1541 self._gitcommand(['merge', '--no-commit', revision]) | 1707 self._gitcommand(['merge', '--no-commit', revision]) |
1542 _sanitize(self.ui, self.wvfs, '.git') | 1708 _sanitize(self.ui, self.wvfs, '.git') |
1543 | 1709 |
1544 if self.dirty(): | 1710 if self.dirty(): |
1545 if self._gitstate() != revision: | 1711 if self._gitstate() != revision: |
1546 dirty = self._gitstate() == self._state[1] or code != 0 | 1712 dirty = self._gitstate() == self._state[1] or code != 0 |
1547 if _updateprompt(self.ui, self, dirty, | 1713 if _updateprompt( |
1548 self._state[1][:7], revision[:7]): | 1714 self.ui, self, dirty, self._state[1][:7], revision[:7] |
1715 ): | |
1549 mergefunc() | 1716 mergefunc() |
1550 else: | 1717 else: |
1551 mergefunc() | 1718 mergefunc() |
1552 | 1719 |
1553 @annotatesubrepoerror | 1720 @annotatesubrepoerror |
1575 | 1742 |
1576 current = self._gitcurrentbranch() | 1743 current = self._gitcurrentbranch() |
1577 if current: | 1744 if current: |
1578 # determine if the current branch is even useful | 1745 # determine if the current branch is even useful |
1579 if not self._gitisancestor(self._state[1], current): | 1746 if not self._gitisancestor(self._state[1], current): |
1580 self.ui.warn(_('unrelated git branch checked out ' | 1747 self.ui.warn( |
1581 'in subrepository "%s"\n') % self._relpath) | 1748 _( |
1749 'unrelated git branch checked out ' | |
1750 'in subrepository "%s"\n' | |
1751 ) | |
1752 % self._relpath | |
1753 ) | |
1582 return False | 1754 return False |
1583 self.ui.status(_('pushing branch %s of subrepository "%s"\n') % | 1755 self.ui.status( |
1584 (current.split('/', 2)[2], self._relpath)) | 1756 _('pushing branch %s of subrepository "%s"\n') |
1757 % (current.split('/', 2)[2], self._relpath) | |
1758 ) | |
1585 ret = self._gitdir(cmd + ['origin', current]) | 1759 ret = self._gitdir(cmd + ['origin', current]) |
1586 return ret[1] == 0 | 1760 return ret[1] == 0 |
1587 else: | 1761 else: |
1588 self.ui.warn(_('no branch checked out in subrepository "%s"\n' | 1762 self.ui.warn( |
1589 'cannot push revision %s\n') % | 1763 _( |
1590 (self._relpath, self._state[1])) | 1764 'no branch checked out in subrepository "%s"\n' |
1765 'cannot push revision %s\n' | |
1766 ) | |
1767 % (self._relpath, self._state[1]) | |
1768 ) | |
1591 return False | 1769 return False |
1592 | 1770 |
1593 @annotatesubrepoerror | 1771 @annotatesubrepoerror |
1594 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): | 1772 def add(self, ui, match, prefix, uipathfn, explicitonly, **opts): |
1595 if self._gitmissing(): | 1773 if self._gitmissing(): |
1611 files = [f for f in sorted(set(files)) if match(f)] | 1789 files = [f for f in sorted(set(files)) if match(f)] |
1612 for f in files: | 1790 for f in files: |
1613 exact = match.exact(f) | 1791 exact = match.exact(f) |
1614 command = ["add"] | 1792 command = ["add"] |
1615 if exact: | 1793 if exact: |
1616 command.append("-f") #should be added, even if ignored | 1794 command.append("-f") # should be added, even if ignored |
1617 if ui.verbose or not exact: | 1795 if ui.verbose or not exact: |
1618 ui.status(_('adding %s\n') % uipathfn(f)) | 1796 ui.status(_('adding %s\n') % uipathfn(f)) |
1619 | 1797 |
1620 if f in tracked: # hg prints 'adding' even if already tracked | 1798 if f in tracked: # hg prints 'adding' even if already tracked |
1621 if exact: | 1799 if exact: |
1632 @annotatesubrepoerror | 1810 @annotatesubrepoerror |
1633 def remove(self): | 1811 def remove(self): |
1634 if self._gitmissing(): | 1812 if self._gitmissing(): |
1635 return | 1813 return |
1636 if self.dirty(): | 1814 if self.dirty(): |
1637 self.ui.warn(_('not removing repo %s because ' | 1815 self.ui.warn( |
1638 'it has changes.\n') % self._relpath) | 1816 _('not removing repo %s because ' 'it has changes.\n') |
1817 % self._relpath | |
1818 ) | |
1639 return | 1819 return |
1640 # we can't fully delete the repository as it may contain | 1820 # we can't fully delete the repository as it may contain |
1641 # local-only history | 1821 # local-only history |
1642 self.ui.note(_('removing subrepo %s\n') % self._relpath) | 1822 self.ui.note(_('removing subrepo %s\n') % self._relpath) |
1643 self._gitcommand(['config', 'core.bare', 'true']) | 1823 self._gitcommand(['config', 'core.bare', 'true']) |
1660 # This should be much faster than manually traversing the trees | 1840 # This should be much faster than manually traversing the trees |
1661 # and objects with many subprocess calls. | 1841 # and objects with many subprocess calls. |
1662 tarstream = self._gitcommand(['archive', revision], stream=True) | 1842 tarstream = self._gitcommand(['archive', revision], stream=True) |
1663 tar = tarfile.open(fileobj=tarstream, mode=r'r|') | 1843 tar = tarfile.open(fileobj=tarstream, mode=r'r|') |
1664 relpath = subrelpath(self) | 1844 relpath = subrelpath(self) |
1665 progress = self.ui.makeprogress(_('archiving (%s)') % relpath, | 1845 progress = self.ui.makeprogress( |
1666 unit=_('files')) | 1846 _('archiving (%s)') % relpath, unit=_('files') |
1847 ) | |
1667 progress.update(0) | 1848 progress.update(0) |
1668 for info in tar: | 1849 for info in tar: |
1669 if info.isdir(): | 1850 if info.isdir(): |
1670 continue | 1851 continue |
1671 bname = pycompat.fsencode(info.name) | 1852 bname = pycompat.fsencode(info.name) |
1679 total += 1 | 1860 total += 1 |
1680 progress.increment() | 1861 progress.increment() |
1681 progress.complete() | 1862 progress.complete() |
1682 return total | 1863 return total |
1683 | 1864 |
1684 | |
1685 @annotatesubrepoerror | 1865 @annotatesubrepoerror |
1686 def cat(self, match, fm, fntemplate, prefix, **opts): | 1866 def cat(self, match, fm, fntemplate, prefix, **opts): |
1687 rev = self._state[1] | 1867 rev = self._state[1] |
1688 if match.anypats(): | 1868 if match.anypats(): |
1689 return 1 #No support for include/exclude yet | 1869 return 1 # No support for include/exclude yet |
1690 | 1870 |
1691 if not match.files(): | 1871 if not match.files(): |
1692 return 1 | 1872 return 1 |
1693 | 1873 |
1694 # TODO: add support for non-plain formatter (see cmdutil.cat()) | 1874 # TODO: add support for non-plain formatter (see cmdutil.cat()) |
1695 for f in match.files(): | 1875 for f in match.files(): |
1696 output = self._gitcommand(["show", "%s:%s" % (rev, f)]) | 1876 output = self._gitcommand(["show", "%s:%s" % (rev, f)]) |
1697 fp = cmdutil.makefileobj(self._ctx, fntemplate, | 1877 fp = cmdutil.makefileobj( |
1698 pathname=self.wvfs.reljoin(prefix, f)) | 1878 self._ctx, fntemplate, pathname=self.wvfs.reljoin(prefix, f) |
1879 ) | |
1699 fp.write(output) | 1880 fp.write(output) |
1700 fp.close() | 1881 fp.close() |
1701 return 0 | 1882 return 0 |
1702 | |
1703 | 1883 |
1704 @annotatesubrepoerror | 1884 @annotatesubrepoerror |
1705 def status(self, rev2, **opts): | 1885 def status(self, rev2, **opts): |
1706 rev1 = self._state[1] | 1886 rev1 = self._state[1] |
1707 if self._gitmissing() or not rev1: | 1887 if self._gitmissing() or not rev1: |
1716 out = self._gitcommand(command) | 1896 out = self._gitcommand(command) |
1717 for line in out.split('\n'): | 1897 for line in out.split('\n'): |
1718 tab = line.find('\t') | 1898 tab = line.find('\t') |
1719 if tab == -1: | 1899 if tab == -1: |
1720 continue | 1900 continue |
1721 status, f = line[tab - 1:tab], line[tab + 1:] | 1901 status, f = line[tab - 1 : tab], line[tab + 1 :] |
1722 if status == 'M': | 1902 if status == 'M': |
1723 modified.append(f) | 1903 modified.append(f) |
1724 elif status == 'A': | 1904 elif status == 'A': |
1725 added.append(f) | 1905 added.append(f) |
1726 elif status == 'D': | 1906 elif status == 'D': |
1741 changedfiles.update(removed) | 1921 changedfiles.update(removed) |
1742 for line in out.split('\0'): | 1922 for line in out.split('\0'): |
1743 if not line: | 1923 if not line: |
1744 continue | 1924 continue |
1745 st = line[0:2] | 1925 st = line[0:2] |
1746 #moves and copies show 2 files on one line | 1926 # moves and copies show 2 files on one line |
1747 if line.find('\0') >= 0: | 1927 if line.find('\0') >= 0: |
1748 filename1, filename2 = line[3:].split('\0') | 1928 filename1, filename2 = line[3:].split('\0') |
1749 else: | 1929 else: |
1750 filename1 = line[3:] | 1930 filename1 = line[3:] |
1751 filename2 = None | 1931 filename2 = None |
1763 out = self._gitcommand(['ls-files']) | 1943 out = self._gitcommand(['ls-files']) |
1764 for f in out.split('\n'): | 1944 for f in out.split('\n'): |
1765 if not f in changedfiles: | 1945 if not f in changedfiles: |
1766 clean.append(f) | 1946 clean.append(f) |
1767 | 1947 |
1768 return scmutil.status(modified, added, removed, deleted, | 1948 return scmutil.status( |
1769 unknown, ignored, clean) | 1949 modified, added, removed, deleted, unknown, ignored, clean |
1950 ) | |
1770 | 1951 |
1771 @annotatesubrepoerror | 1952 @annotatesubrepoerror |
1772 def diff(self, ui, diffopts, node2, match, prefix, **opts): | 1953 def diff(self, ui, diffopts, node2, match, prefix, **opts): |
1773 node1 = self._state[1] | 1954 node1 = self._state[1] |
1774 cmd = ['diff', '--no-renames'] | 1955 cmd = ['diff', '--no-renames'] |
1777 else: | 1958 else: |
1778 # for Git, this also implies '-p' | 1959 # for Git, this also implies '-p' |
1779 cmd.append('-U%d' % diffopts.context) | 1960 cmd.append('-U%d' % diffopts.context) |
1780 | 1961 |
1781 if diffopts.noprefix: | 1962 if diffopts.noprefix: |
1782 cmd.extend(['--src-prefix=%s/' % prefix, | 1963 cmd.extend( |
1783 '--dst-prefix=%s/' % prefix]) | 1964 ['--src-prefix=%s/' % prefix, '--dst-prefix=%s/' % prefix] |
1965 ) | |
1784 else: | 1966 else: |
1785 cmd.extend(['--src-prefix=a/%s/' % prefix, | 1967 cmd.extend( |
1786 '--dst-prefix=b/%s/' % prefix]) | 1968 ['--src-prefix=a/%s/' % prefix, '--dst-prefix=b/%s/' % prefix] |
1969 ) | |
1787 | 1970 |
1788 if diffopts.ignorews: | 1971 if diffopts.ignorews: |
1789 cmd.append('--ignore-all-space') | 1972 cmd.append('--ignore-all-space') |
1790 if diffopts.ignorewsamount: | 1973 if diffopts.ignorewsamount: |
1791 cmd.append('--ignore-space-change') | 1974 cmd.append('--ignore-space-change') |
1792 if (self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) | 1975 if ( |
1793 and diffopts.ignoreblanklines): | 1976 self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) |
1977 and diffopts.ignoreblanklines | |
1978 ): | |
1794 cmd.append('--ignore-blank-lines') | 1979 cmd.append('--ignore-blank-lines') |
1795 | 1980 |
1796 cmd.append(node1) | 1981 cmd.append(node1) |
1797 if node2: | 1982 if node2: |
1798 cmd.append(node2) | 1983 cmd.append(node2) |
1818 names = status.modified | 2003 names = status.modified |
1819 for name in names: | 2004 for name in names: |
1820 # backuppath() expects a path relative to the parent repo (the | 2005 # backuppath() expects a path relative to the parent repo (the |
1821 # repo that ui.origbackuppath is relative to) | 2006 # repo that ui.origbackuppath is relative to) |
1822 parentname = os.path.join(self._path, name) | 2007 parentname = os.path.join(self._path, name) |
1823 bakname = scmutil.backuppath(self.ui, self._subparent, | 2008 bakname = scmutil.backuppath( |
1824 parentname) | 2009 self.ui, self._subparent, parentname |
1825 self.ui.note(_('saving current version of %s as %s\n') % | 2010 ) |
1826 (name, os.path.relpath(bakname))) | 2011 self.ui.note( |
2012 _('saving current version of %s as %s\n') | |
2013 % (name, os.path.relpath(bakname)) | |
2014 ) | |
1827 util.rename(self.wvfs.join(name), bakname) | 2015 util.rename(self.wvfs.join(name), bakname) |
1828 | 2016 |
1829 if not opts.get(r'dry_run'): | 2017 if not opts.get(r'dry_run'): |
1830 self.get(substate, overwrite=True) | 2018 self.get(substate, overwrite=True) |
1831 return [] | 2019 return [] |
1832 | 2020 |
1833 def shortid(self, revid): | 2021 def shortid(self, revid): |
1834 return revid[:7] | 2022 return revid[:7] |
2023 | |
1835 | 2024 |
1836 types = { | 2025 types = { |
1837 'hg': hgsubrepo, | 2026 'hg': hgsubrepo, |
1838 'svn': svnsubrepo, | 2027 'svn': svnsubrepo, |
1839 'git': gitsubrepo, | 2028 'git': gitsubrepo, |
1840 } | 2029 } |