comparison mercurial/subrepo.py @ 23572:40e62fbd7356

subrepo: rename the '_ui' member to 'ui' The localrepository class has a 'ui' member, so keeping the names the same will allow for duck typing with subrepo instances when accessing 'ui'. Changing this is easier than finding all of the localrepository instance uses and renaming that to '_ui'.
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 13 Dec 2014 15:19:38 -0500
parents 9626120e017b
children 3fec2a3c768b
comparison
equal deleted inserted replaced
23571:9626120e017b 23572:40e62fbd7356
372 # subrepo classes need to implement the following abstract class: 372 # subrepo classes need to implement the following abstract class:
373 373
374 class abstractsubrepo(object): 374 class abstractsubrepo(object):
375 375
376 def __init__(self, ui): 376 def __init__(self, ui):
377 self._ui = ui 377 self.ui = ui
378 378
379 def storeclean(self, path): 379 def storeclean(self, path):
380 """ 380 """
381 returns true if the repository has not changed since it was last 381 returns true if the repository has not changed since it was last
382 cloned from or pushed to a given repository. 382 cloned from or pushed to a given repository.
437 437
438 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly): 438 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
439 return [] 439 return []
440 440
441 def addremove(self, matcher, prefix, opts, dry_run, similarity): 441 def addremove(self, matcher, prefix, opts, dry_run, similarity):
442 self._ui.warn("%s: %s" % (prefix, _("addremove is not supported"))) 442 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
443 return 1 443 return 1
444 444
445 def cat(self, ui, match, prefix, **opts): 445 def cat(self, ui, match, prefix, **opts):
446 return 1 446 return 1
447 447
907 % self._path) 907 % self._path)
908 908
909 def _svncommand(self, commands, filename='', failok=False): 909 def _svncommand(self, commands, filename='', failok=False):
910 cmd = [self._exe] 910 cmd = [self._exe]
911 extrakw = {} 911 extrakw = {}
912 if not self._ui.interactive(): 912 if not self.ui.interactive():
913 # Making stdin be a pipe should prevent svn from behaving 913 # Making stdin be a pipe should prevent svn from behaving
914 # interactively even if we can't pass --non-interactive. 914 # interactively even if we can't pass --non-interactive.
915 extrakw['stdin'] = subprocess.PIPE 915 extrakw['stdin'] = subprocess.PIPE
916 # Starting in svn 1.5 --non-interactive is a global flag 916 # Starting in svn 1.5 --non-interactive is a global flag
917 # instead of being per-command, but we need to support 1.4 so 917 # instead of being per-command, but we need to support 1.4 so
937 stderr = stderr.strip() 937 stderr = stderr.strip()
938 if not failok: 938 if not failok:
939 if p.returncode: 939 if p.returncode:
940 raise util.Abort(stderr or 'exited with code %d' % p.returncode) 940 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
941 if stderr: 941 if stderr:
942 self._ui.warn(stderr + '\n') 942 self.ui.warn(stderr + '\n')
943 return stdout, stderr 943 return stdout, stderr
944 944
945 @propertycache 945 @propertycache
946 def _svnversion(self): 946 def _svnversion(self):
947 output, err = self._svncommand(['--version', '--quiet'], filename=None) 947 output, err = self._svncommand(['--version', '--quiet'], filename=None)
1029 if missing: 1029 if missing:
1030 # svn can commit with missing entries but aborting like hg 1030 # svn can commit with missing entries but aborting like hg
1031 # seems a better approach. 1031 # seems a better approach.
1032 raise util.Abort(_('cannot commit missing svn entries')) 1032 raise util.Abort(_('cannot commit missing svn entries'))
1033 commitinfo, err = self._svncommand(['commit', '-m', text]) 1033 commitinfo, err = self._svncommand(['commit', '-m', text])
1034 self._ui.status(commitinfo) 1034 self.ui.status(commitinfo)
1035 newrev = re.search('Committed revision ([0-9]+).', commitinfo) 1035 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1036 if not newrev: 1036 if not newrev:
1037 if not commitinfo.strip(): 1037 if not commitinfo.strip():
1038 # Sometimes, our definition of "changed" differs from 1038 # Sometimes, our definition of "changed" differs from
1039 # svn one. For instance, svn ignores missing files 1039 # svn one. For instance, svn ignores missing files
1040 # when committing. If there are only missing files, no 1040 # when committing. If there are only missing files, no
1041 # commit is made, no output and no error code. 1041 # commit is made, no output and no error code.
1042 raise util.Abort(_('failed to commit svn changes')) 1042 raise util.Abort(_('failed to commit svn changes'))
1043 raise util.Abort(commitinfo.splitlines()[-1]) 1043 raise util.Abort(commitinfo.splitlines()[-1])
1044 newrev = newrev.groups()[0] 1044 newrev = newrev.groups()[0]
1045 self._ui.status(self._svncommand(['update', '-r', newrev])[0]) 1045 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1046 return newrev 1046 return newrev
1047 1047
1048 @annotatesubrepoerror 1048 @annotatesubrepoerror
1049 def remove(self): 1049 def remove(self):
1050 if self.dirty(): 1050 if self.dirty():
1051 self._ui.warn(_('not removing repo %s because ' 1051 self.ui.warn(_('not removing repo %s because '
1052 'it has changes.\n') % self._path) 1052 'it has changes.\n') % self._path)
1053 return 1053 return
1054 self._ui.note(_('removing subrepo %s\n') % self._path) 1054 self.ui.note(_('removing subrepo %s\n') % self._path)
1055 1055
1056 def onerror(function, path, excinfo): 1056 def onerror(function, path, excinfo):
1057 if function is not os.remove: 1057 if function is not os.remove:
1058 raise 1058 raise
1059 # read-only files cannot be unlinked under Windows 1059 # read-only files cannot be unlinked under Windows
1079 args.append('--force') 1079 args.append('--force')
1080 # The revision must be specified at the end of the URL to properly 1080 # The revision must be specified at the end of the URL to properly
1081 # update to a directory which has since been deleted and recreated. 1081 # update to a directory which has since been deleted and recreated.
1082 args.append('%s@%s' % (state[0], state[1])) 1082 args.append('%s@%s' % (state[0], state[1]))
1083 status, err = self._svncommand(args, failok=True) 1083 status, err = self._svncommand(args, failok=True)
1084 _sanitize(self._ui, self._ctx._repo.wjoin(self._path), '.svn') 1084 _sanitize(self.ui, self._ctx._repo.wjoin(self._path), '.svn')
1085 if not re.search('Checked out revision [0-9]+.', status): 1085 if not re.search('Checked out revision [0-9]+.', status):
1086 if ('is already a working copy for a different URL' in err 1086 if ('is already a working copy for a different URL' in err
1087 and (self._wcchanged()[:2] == (False, False))): 1087 and (self._wcchanged()[:2] == (False, False))):
1088 # obstructed but clean working copy, so just blow it away. 1088 # obstructed but clean working copy, so just blow it away.
1089 self.remove() 1089 self.remove()
1090 self.get(state, overwrite=False) 1090 self.get(state, overwrite=False)
1091 return 1091 return
1092 raise util.Abort((status or err).splitlines()[-1]) 1092 raise util.Abort((status or err).splitlines()[-1])
1093 self._ui.status(status) 1093 self.ui.status(status)
1094 1094
1095 @annotatesubrepoerror 1095 @annotatesubrepoerror
1096 def merge(self, state): 1096 def merge(self, state):
1097 old = self._state[1] 1097 old = self._state[1]
1098 new = state[1] 1098 new = state[1]
1099 wcrev = self._wcrev() 1099 wcrev = self._wcrev()
1100 if new != wcrev: 1100 if new != wcrev:
1101 dirty = old == wcrev or self._wcchanged()[0] 1101 dirty = old == wcrev or self._wcchanged()[0]
1102 if _updateprompt(self._ui, self, dirty, wcrev, new): 1102 if _updateprompt(self.ui, self, dirty, wcrev, new):
1103 self.get(state, False) 1103 self.get(state, False)
1104 1104
1105 def push(self, opts): 1105 def push(self, opts):
1106 # push is a no-op for SVN 1106 # push is a no-op for SVN
1107 return True 1107 return True
1145 raise 1145 raise
1146 self._gitexecutable = 'git.cmd' 1146 self._gitexecutable = 'git.cmd'
1147 out, err = self._gitnodir(['--version']) 1147 out, err = self._gitnodir(['--version'])
1148 versionstatus = self._checkversion(out) 1148 versionstatus = self._checkversion(out)
1149 if versionstatus == 'unknown': 1149 if versionstatus == 'unknown':
1150 self._ui.warn(_('cannot retrieve git version\n')) 1150 self.ui.warn(_('cannot retrieve git version\n'))
1151 elif versionstatus == 'abort': 1151 elif versionstatus == 'abort':
1152 raise util.Abort(_('git subrepo requires at least 1.6.0 or later')) 1152 raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
1153 elif versionstatus == 'warning': 1153 elif versionstatus == 'warning':
1154 self._ui.warn(_('git subrepo requires at least 1.6.0 or later\n')) 1154 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1155 1155
1156 @staticmethod 1156 @staticmethod
1157 def _gitversion(out): 1157 def _gitversion(out):
1158 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out) 1158 m = re.search(r'^git version (\d+)\.(\d+)\.(\d+)', out)
1159 if m: 1159 if m:
1212 """Calls the git command 1212 """Calls the git command
1213 1213
1214 The methods tries to call the git command. versions prior to 1.6.0 1214 The methods tries to call the git command. versions prior to 1.6.0
1215 are not supported and very probably fail. 1215 are not supported and very probably fail.
1216 """ 1216 """
1217 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands))) 1217 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1218 # unless ui.quiet is set, print git's stderr, 1218 # unless ui.quiet is set, print git's stderr,
1219 # which is mostly progress and useful info 1219 # which is mostly progress and useful info
1220 errpipe = None 1220 errpipe = None
1221 if self._ui.quiet: 1221 if self.ui.quiet:
1222 errpipe = open(os.devnull, 'w') 1222 errpipe = open(os.devnull, 'w')
1223 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1, 1223 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1224 cwd=cwd, env=env, close_fds=util.closefds, 1224 cwd=cwd, env=env, close_fds=util.closefds,
1225 stdout=subprocess.PIPE, stderr=errpipe) 1225 stdout=subprocess.PIPE, stderr=errpipe)
1226 if stream: 1226 if stream:
1321 return _abssource(self) 1321 return _abssource(self)
1322 1322
1323 def _fetch(self, source, revision): 1323 def _fetch(self, source, revision):
1324 if self._gitmissing(): 1324 if self._gitmissing():
1325 source = self._abssource(source) 1325 source = self._abssource(source)
1326 self._ui.status(_('cloning subrepo %s from %s\n') % 1326 self.ui.status(_('cloning subrepo %s from %s\n') %
1327 (self._relpath, source)) 1327 (self._relpath, source))
1328 self._gitnodir(['clone', source, self._abspath]) 1328 self._gitnodir(['clone', source, self._abspath])
1329 if self._githavelocally(revision): 1329 if self._githavelocally(revision):
1330 return 1330 return
1331 self._ui.status(_('pulling subrepo %s from %s\n') % 1331 self.ui.status(_('pulling subrepo %s from %s\n') %
1332 (self._relpath, self._gitremote('origin'))) 1332 (self._relpath, self._gitremote('origin')))
1333 # try only origin: the originally cloned repo 1333 # try only origin: the originally cloned repo
1334 self._gitcommand(['fetch']) 1334 self._gitcommand(['fetch'])
1335 if not self._githavelocally(revision): 1335 if not self._githavelocally(revision):
1336 raise util.Abort(_("revision %s does not exist in subrepo %s\n") % 1336 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
1383 # the -f option will otherwise throw away files added for 1383 # the -f option will otherwise throw away files added for
1384 # commit, not just unmark them. 1384 # commit, not just unmark them.
1385 self._gitcommand(['reset', 'HEAD']) 1385 self._gitcommand(['reset', 'HEAD'])
1386 cmd.append('-f') 1386 cmd.append('-f')
1387 self._gitcommand(cmd + args) 1387 self._gitcommand(cmd + args)
1388 _sanitize(self._ui, self._abspath, '.git') 1388 _sanitize(self.ui, self._abspath, '.git')
1389 1389
1390 def rawcheckout(): 1390 def rawcheckout():
1391 # no branch to checkout, check it out with no branch 1391 # no branch to checkout, check it out with no branch
1392 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') % 1392 self.ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1393 self._relpath) 1393 self._relpath)
1394 self._ui.warn(_('check out a git branch if you intend ' 1394 self.ui.warn(_('check out a git branch if you intend '
1395 'to make changes\n')) 1395 'to make changes\n'))
1396 checkout(['-q', revision]) 1396 checkout(['-q', revision])
1397 1397
1398 if revision not in rev2branch: 1398 if revision not in rev2branch:
1399 rawcheckout() 1399 rawcheckout()
1432 # Since we are only looking at branching at update, we need to 1432 # Since we are only looking at branching at update, we need to
1433 # detect this situation and perform this action lazily. 1433 # detect this situation and perform this action lazily.
1434 if tracking[remote] != self._gitcurrentbranch(): 1434 if tracking[remote] != self._gitcurrentbranch():
1435 checkout([tracking[remote]]) 1435 checkout([tracking[remote]])
1436 self._gitcommand(['merge', '--ff', remote]) 1436 self._gitcommand(['merge', '--ff', remote])
1437 _sanitize(self._ui, self._abspath, '.git') 1437 _sanitize(self.ui, self._abspath, '.git')
1438 else: 1438 else:
1439 # a real merge would be required, just checkout the revision 1439 # a real merge would be required, just checkout the revision
1440 rawcheckout() 1440 rawcheckout()
1441 1441
1442 @annotatesubrepoerror 1442 @annotatesubrepoerror
1468 def mergefunc(): 1468 def mergefunc():
1469 if base == revision: 1469 if base == revision:
1470 self.get(state) # fast forward merge 1470 self.get(state) # fast forward merge
1471 elif base != self._state[1]: 1471 elif base != self._state[1]:
1472 self._gitcommand(['merge', '--no-commit', revision]) 1472 self._gitcommand(['merge', '--no-commit', revision])
1473 _sanitize(self._ui, self._abspath, '.git') 1473 _sanitize(self.ui, self._abspath, '.git')
1474 1474
1475 if self.dirty(): 1475 if self.dirty():
1476 if self._gitstate() != revision: 1476 if self._gitstate() != revision:
1477 dirty = self._gitstate() == self._state[1] or code != 0 1477 dirty = self._gitstate() == self._state[1] or code != 0
1478 if _updateprompt(self._ui, self, dirty, 1478 if _updateprompt(self.ui, self, dirty,
1479 self._state[1][:7], revision[:7]): 1479 self._state[1][:7], revision[:7]):
1480 mergefunc() 1480 mergefunc()
1481 else: 1481 else:
1482 mergefunc() 1482 mergefunc()
1483 1483
1506 1506
1507 current = self._gitcurrentbranch() 1507 current = self._gitcurrentbranch()
1508 if current: 1508 if current:
1509 # determine if the current branch is even useful 1509 # determine if the current branch is even useful
1510 if not self._gitisancestor(self._state[1], current): 1510 if not self._gitisancestor(self._state[1], current):
1511 self._ui.warn(_('unrelated git branch checked out ' 1511 self.ui.warn(_('unrelated git branch checked out '
1512 'in subrepo %s\n') % self._relpath) 1512 'in subrepo %s\n') % self._relpath)
1513 return False 1513 return False
1514 self._ui.status(_('pushing branch %s of subrepo %s\n') % 1514 self.ui.status(_('pushing branch %s of subrepo %s\n') %
1515 (current.split('/', 2)[2], self._relpath)) 1515 (current.split('/', 2)[2], self._relpath))
1516 ret = self._gitdir(cmd + ['origin', current]) 1516 ret = self._gitdir(cmd + ['origin', current])
1517 return ret[1] == 0 1517 return ret[1] == 0
1518 else: 1518 else:
1519 self._ui.warn(_('no branch checked out in subrepo %s\n' 1519 self.ui.warn(_('no branch checked out in subrepo %s\n'
1520 'cannot push revision %s\n') % 1520 'cannot push revision %s\n') %
1521 (self._relpath, self._state[1])) 1521 (self._relpath, self._state[1]))
1522 return False 1522 return False
1523 1523
1524 @annotatesubrepoerror 1524 @annotatesubrepoerror
1525 def remove(self): 1525 def remove(self):
1526 if self._gitmissing(): 1526 if self._gitmissing():
1527 return 1527 return
1528 if self.dirty(): 1528 if self.dirty():
1529 self._ui.warn(_('not removing repo %s because ' 1529 self.ui.warn(_('not removing repo %s because '
1530 'it has changes.\n') % self._relpath) 1530 'it has changes.\n') % self._relpath)
1531 return 1531 return
1532 # we can't fully delete the repository as it may contain 1532 # we can't fully delete the repository as it may contain
1533 # local-only history 1533 # local-only history
1534 self._ui.note(_('removing subrepo %s\n') % self._relpath) 1534 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1535 self._gitcommand(['config', 'core.bare', 'true']) 1535 self._gitcommand(['config', 'core.bare', 'true'])
1536 for f in os.listdir(self._abspath): 1536 for f in os.listdir(self._abspath):
1537 if f == '.git': 1537 if f == '.git':
1538 continue 1538 continue
1539 path = os.path.join(self._abspath, f) 1539 path = os.path.join(self._abspath, f)