mercurial/hg.py
branchstable
changeset 14687 15200b46165b
parent 14607 bd1acea552ff
child 14737 b39ed8c8e5e5
equal deleted inserted replaced
14508:07722bb8a08c 14687:15200b46165b
     6 # This software may be used and distributed according to the terms of the
     6 # This software may be used and distributed according to the terms of the
     7 # GNU General Public License version 2 or any later version.
     7 # GNU General Public License version 2 or any later version.
     8 
     8 
     9 from i18n import _
     9 from i18n import _
    10 from lock import release
    10 from lock import release
    11 from node import hex, nullid, nullrev, short
    11 from node import hex, nullid
    12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
    12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
    13 import lock, util, extensions, error, encoding, node
    13 import lock, util, extensions, error, node
    14 import cmdutil, discovery, url
    14 import cmdutil, discovery
    15 import merge as mergemod
    15 import merge as mergemod
    16 import verify as verifymod
    16 import verify as verifymod
    17 import errno, os, shutil
    17 import errno, os, shutil
    18 
    18 
    19 def _local(path):
    19 def _local(path):
    20     path = util.expandpath(util.drop_scheme('file', path))
    20     path = util.expandpath(util.localpath(path))
    21     return (os.path.isfile(path) and bundlerepo or localrepo)
    21     return (os.path.isfile(path) and bundlerepo or localrepo)
    22 
    22 
    23 def addbranchrevs(lrepo, repo, branches, revs):
    23 def addbranchrevs(lrepo, repo, branches, revs):
    24     hashbranch, branches = branches
    24     hashbranch, branches = branches
    25     if not hashbranch and not branches:
    25     if not hashbranch and not branches:
    49     if hashbranch:
    49     if hashbranch:
    50         if not primary(hashbranch):
    50         if not primary(hashbranch):
    51             revs.append(hashbranch)
    51             revs.append(hashbranch)
    52     return revs, revs[0]
    52     return revs, revs[0]
    53 
    53 
    54 def parseurl(url, branches=None):
    54 def parseurl(path, branches=None):
    55     '''parse url#branch, returning (url, (branch, branches))'''
    55     '''parse url#branch, returning (url, (branch, branches))'''
    56 
    56 
    57     if '#' not in url:
    57     u = util.url(path)
    58         return url, (None, branches or [])
    58     branch = None
    59     url, branch = url.split('#', 1)
    59     if u.fragment:
    60     return url, (branch, branches or [])
    60         branch = u.fragment
       
    61         u.fragment = None
       
    62     return str(u), (branch, branches or [])
    61 
    63 
    62 schemes = {
    64 schemes = {
    63     'bundle': bundlerepo,
    65     'bundle': bundlerepo,
    64     'file': _local,
    66     'file': _local,
    65     'http': httprepo,
    67     'http': httprepo,
    66     'https': httprepo,
    68     'https': httprepo,
    67     'ssh': sshrepo,
    69     'ssh': sshrepo,
    68     'static-http': statichttprepo,
    70     'static-http': statichttprepo,
    69 }
    71 }
    70 
    72 
    71 def _lookup(path):
    73 def _peerlookup(path):
    72     scheme = 'file'
    74     u = util.url(path)
    73     if path:
    75     scheme = u.scheme or 'file'
    74         c = path.find(':')
       
    75         if c > 0:
       
    76             scheme = path[:c]
       
    77     thing = schemes.get(scheme) or schemes['file']
    76     thing = schemes.get(scheme) or schemes['file']
    78     try:
    77     try:
    79         return thing(path)
    78         return thing(path)
    80     except TypeError:
    79     except TypeError:
    81         return thing
    80         return thing
    82 
    81 
    83 def islocal(repo):
    82 def islocal(repo):
    84     '''return true if repo or path is local'''
    83     '''return true if repo or path is local'''
    85     if isinstance(repo, str):
    84     if isinstance(repo, str):
    86         try:
    85         try:
    87             return _lookup(repo).islocal(repo)
    86             return _peerlookup(repo).islocal(repo)
    88         except AttributeError:
    87         except AttributeError:
    89             return False
    88             return False
    90     return repo.local()
    89     return repo.local()
    91 
    90 
    92 def repository(ui, path='', create=False):
    91 def repository(ui, path='', create=False):
    93     """return a repository object for the specified path"""
    92     """return a repository object for the specified path"""
    94     repo = _lookup(path).instance(ui, path, create)
    93     repo = _peerlookup(path).instance(ui, path, create)
    95     ui = getattr(repo, "ui", ui)
    94     ui = getattr(repo, "ui", ui)
    96     for name, module in extensions.extensions():
    95     for name, module in extensions.extensions():
    97         hook = getattr(module, 'reposetup', None)
    96         hook = getattr(module, 'reposetup', None)
    98         if hook:
    97         if hook:
    99             hook(ui, repo)
    98             hook(ui, repo)
   100     return repo
    99     return repo
   101 
   100 
       
   101 def peer(ui, opts, path, create=False):
       
   102     '''return a repository peer for the specified path'''
       
   103     rui = remoteui(ui, opts)
       
   104     return _peerlookup(path).instance(rui, path, create)
       
   105 
   102 def defaultdest(source):
   106 def defaultdest(source):
   103     '''return default destination of clone if none is given'''
   107     '''return default destination of clone if none is given'''
   104     return os.path.basename(os.path.normpath(source))
   108     return os.path.basename(os.path.normpath(source))
   105 
       
   106 def localpath(path):
       
   107     if path.startswith('file://localhost/'):
       
   108         return path[16:]
       
   109     if path.startswith('file://'):
       
   110         return path[7:]
       
   111     if path.startswith('file:'):
       
   112         return path[5:]
       
   113     return path
       
   114 
   109 
   115 def share(ui, source, dest=None, update=True):
   110 def share(ui, source, dest=None, update=True):
   116     '''create a shared repository'''
   111     '''create a shared repository'''
   117 
   112 
   118     if not islocal(source):
   113     if not islocal(source):
   141     if os.path.exists(roothg):
   136     if os.path.exists(roothg):
   142         raise util.Abort(_('destination already exists'))
   137         raise util.Abort(_('destination already exists'))
   143 
   138 
   144     if not os.path.isdir(root):
   139     if not os.path.isdir(root):
   145         os.mkdir(root)
   140         os.mkdir(root)
   146     os.mkdir(roothg)
   141     util.makedir(roothg, notindexed=True)
   147 
   142 
   148     requirements = ''
   143     requirements = ''
   149     try:
   144     try:
   150         requirements = srcrepo.opener('requires').read()
   145         requirements = srcrepo.opener.read('requires')
   151     except IOError, inst:
   146     except IOError, inst:
   152         if inst.errno != errno.ENOENT:
   147         if inst.errno != errno.ENOENT:
   153             raise
   148             raise
   154 
   149 
   155     requirements += 'shared\n'
   150     requirements += 'shared\n'
   156     file(os.path.join(roothg, 'requires'), 'w').write(requirements)
   151     util.writefile(os.path.join(roothg, 'requires'), requirements)
   157     file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
   152     util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
       
   153 
       
   154     r = repository(ui, root)
   158 
   155 
   159     default = srcrepo.ui.config('paths', 'default')
   156     default = srcrepo.ui.config('paths', 'default')
   160     if default:
   157     if default:
   161         f = file(os.path.join(roothg, 'hgrc'), 'w')
   158         fp = r.opener("hgrc", "w", text=True)
   162         f.write('[paths]\ndefault = %s\n' % default)
   159         fp.write("[paths]\n")
   163         f.close()
   160         fp.write("default = %s\n" % default)
   164 
   161         fp.close()
   165     r = repository(ui, root)
       
   166 
   162 
   167     if update:
   163     if update:
   168         r.ui.status(_("updating working directory\n"))
   164         r.ui.status(_("updating working directory\n"))
   169         if update is not True:
   165         if update is not True:
   170             checkout = update
   166             checkout = update
   176                 break
   172                 break
   177             except error.RepoLookupError:
   173             except error.RepoLookupError:
   178                 continue
   174                 continue
   179         _update(r, uprev)
   175         _update(r, uprev)
   180 
   176 
   181 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
   177 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
   182           stream=False, branch=None):
   178           update=True, stream=False, branch=None):
   183     """Make a copy of an existing repository.
   179     """Make a copy of an existing repository.
   184 
   180 
   185     Create a copy of an existing repository in a new directory.  The
   181     Create a copy of an existing repository in a new directory.  The
   186     source and destination are URLs, as passed to the repository
   182     source and destination are URLs, as passed to the repository
   187     function.  Returns a pair of repository objects, the source and
   183     function.  Returns a pair of repository objects, the source and
   216     """
   212     """
   217 
   213 
   218     if isinstance(source, str):
   214     if isinstance(source, str):
   219         origsource = ui.expandpath(source)
   215         origsource = ui.expandpath(source)
   220         source, branch = parseurl(origsource, branch)
   216         source, branch = parseurl(origsource, branch)
   221         src_repo = repository(ui, source)
   217         srcrepo = repository(remoteui(ui, peeropts), source)
   222     else:
   218     else:
   223         src_repo = source
   219         srcrepo = source
   224         branch = (None, branch or [])
   220         branch = (None, branch or [])
   225         origsource = source = src_repo.url()
   221         origsource = source = srcrepo.url()
   226     rev, checkout = addbranchrevs(src_repo, src_repo, branch, rev)
   222     rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
   227 
   223 
   228     if dest is None:
   224     if dest is None:
   229         dest = defaultdest(source)
   225         dest = defaultdest(source)
   230         ui.status(_("destination directory: %s\n") % dest)
   226         ui.status(_("destination directory: %s\n") % dest)
   231     else:
   227     else:
   232         dest = ui.expandpath(dest)
   228         dest = ui.expandpath(dest)
   233 
   229 
   234     dest = localpath(dest)
   230     dest = util.localpath(dest)
   235     source = localpath(source)
   231     source = util.localpath(source)
   236 
   232 
   237     if os.path.exists(dest):
   233     if os.path.exists(dest):
   238         if not os.path.isdir(dest):
   234         if not os.path.isdir(dest):
   239             raise util.Abort(_("destination '%s' already exists") % dest)
   235             raise util.Abort(_("destination '%s' already exists") % dest)
   240         elif os.listdir(dest):
   236         elif os.listdir(dest):
   248             self.dir_ = None
   244             self.dir_ = None
   249         def cleanup(self):
   245         def cleanup(self):
   250             if self.dir_:
   246             if self.dir_:
   251                 self.rmtree(self.dir_, True)
   247                 self.rmtree(self.dir_, True)
   252 
   248 
   253     src_lock = dest_lock = dir_cleanup = None
   249     srclock = destlock = dircleanup = None
   254     try:
   250     try:
       
   251         abspath = origsource
       
   252         if islocal(origsource):
       
   253             abspath = os.path.abspath(util.localpath(origsource))
       
   254 
   255         if islocal(dest):
   255         if islocal(dest):
   256             dir_cleanup = DirCleanup(dest)
   256             dircleanup = DirCleanup(dest)
   257 
   257 
   258         abspath = origsource
       
   259         copy = False
   258         copy = False
   260         if src_repo.cancopy() and islocal(dest):
   259         if srcrepo.cancopy() and islocal(dest):
   261             abspath = os.path.abspath(util.drop_scheme('file', origsource))
       
   262             copy = not pull and not rev
   260             copy = not pull and not rev
   263 
   261 
   264         if copy:
   262         if copy:
   265             try:
   263             try:
   266                 # we use a lock here because if we race with commit, we
   264                 # we use a lock here because if we race with commit, we
   267                 # can end up with extra data in the cloned revlogs that's
   265                 # can end up with extra data in the cloned revlogs that's
   268                 # not pointed to by changesets, thus causing verify to
   266                 # not pointed to by changesets, thus causing verify to
   269                 # fail
   267                 # fail
   270                 src_lock = src_repo.lock(wait=False)
   268                 srclock = srcrepo.lock(wait=False)
   271             except error.LockError:
   269             except error.LockError:
   272                 copy = False
   270                 copy = False
   273 
   271 
   274         if copy:
   272         if copy:
   275             src_repo.hook('preoutgoing', throw=True, source='clone')
   273             srcrepo.hook('preoutgoing', throw=True, source='clone')
   276             hgdir = os.path.realpath(os.path.join(dest, ".hg"))
   274             hgdir = os.path.realpath(os.path.join(dest, ".hg"))
   277             if not os.path.exists(dest):
   275             if not os.path.exists(dest):
   278                 os.mkdir(dest)
   276                 os.mkdir(dest)
   279             else:
   277             else:
   280                 # only clean up directories we create ourselves
   278                 # only clean up directories we create ourselves
   281                 dir_cleanup.dir_ = hgdir
   279                 dircleanup.dir_ = hgdir
   282             try:
   280             try:
   283                 dest_path = hgdir
   281                 destpath = hgdir
   284                 os.mkdir(dest_path)
   282                 util.makedir(destpath, notindexed=True)
   285             except OSError, inst:
   283             except OSError, inst:
   286                 if inst.errno == errno.EEXIST:
   284                 if inst.errno == errno.EEXIST:
   287                     dir_cleanup.close()
   285                     dircleanup.close()
   288                     raise util.Abort(_("destination '%s' already exists")
   286                     raise util.Abort(_("destination '%s' already exists")
   289                                      % dest)
   287                                      % dest)
   290                 raise
   288                 raise
   291 
   289 
   292             hardlink = None
   290             hardlink = None
   293             num = 0
   291             num = 0
   294             for f in src_repo.store.copylist():
   292             for f in srcrepo.store.copylist():
   295                 src = os.path.join(src_repo.sharedpath, f)
   293                 src = os.path.join(srcrepo.sharedpath, f)
   296                 dst = os.path.join(dest_path, f)
   294                 dst = os.path.join(destpath, f)
   297                 dstbase = os.path.dirname(dst)
   295                 dstbase = os.path.dirname(dst)
   298                 if dstbase and not os.path.exists(dstbase):
   296                 if dstbase and not os.path.exists(dstbase):
   299                     os.mkdir(dstbase)
   297                     os.mkdir(dstbase)
   300                 if os.path.exists(src):
   298                 if os.path.exists(src):
   301                     if dst.endswith('data'):
   299                     if dst.endswith('data'):
   302                         # lock to avoid premature writing to the target
   300                         # lock to avoid premature writing to the target
   303                         dest_lock = lock.lock(os.path.join(dstbase, "lock"))
   301                         destlock = lock.lock(os.path.join(dstbase, "lock"))
   304                     hardlink, n = util.copyfiles(src, dst, hardlink)
   302                     hardlink, n = util.copyfiles(src, dst, hardlink)
   305                     num += n
   303                     num += n
   306             if hardlink:
   304             if hardlink:
   307                 ui.debug("linked %d files\n" % num)
   305                 ui.debug("linked %d files\n" % num)
   308             else:
   306             else:
   309                 ui.debug("copied %d files\n" % num)
   307                 ui.debug("copied %d files\n" % num)
   310 
   308 
   311             # we need to re-init the repo after manually copying the data
   309             # we need to re-init the repo after manually copying the data
   312             # into it
   310             # into it
   313             dest_repo = repository(ui, dest)
   311             destrepo = repository(remoteui(ui, peeropts), dest)
   314             src_repo.hook('outgoing', source='clone',
   312             srcrepo.hook('outgoing', source='clone',
   315                           node=node.hex(node.nullid))
   313                           node=node.hex(node.nullid))
   316         else:
   314         else:
   317             try:
   315             try:
   318                 dest_repo = repository(ui, dest, create=True)
   316                 destrepo = repository(remoteui(ui, peeropts), dest,
       
   317                                       create=True)
   319             except OSError, inst:
   318             except OSError, inst:
   320                 if inst.errno == errno.EEXIST:
   319                 if inst.errno == errno.EEXIST:
   321                     dir_cleanup.close()
   320                     dircleanup.close()
   322                     raise util.Abort(_("destination '%s' already exists")
   321                     raise util.Abort(_("destination '%s' already exists")
   323                                      % dest)
   322                                      % dest)
   324                 raise
   323                 raise
   325 
   324 
   326             revs = None
   325             revs = None
   327             if rev:
   326             if rev:
   328                 if 'lookup' not in src_repo.capabilities:
   327                 if not srcrepo.capable('lookup'):
   329                     raise util.Abort(_("src repository does not support "
   328                     raise util.Abort(_("src repository does not support "
   330                                        "revision lookup and so doesn't "
   329                                        "revision lookup and so doesn't "
   331                                        "support clone by revision"))
   330                                        "support clone by revision"))
   332                 revs = [src_repo.lookup(r) for r in rev]
   331                 revs = [srcrepo.lookup(r) for r in rev]
   333                 checkout = revs[0]
   332                 checkout = revs[0]
   334             if dest_repo.local():
   333             if destrepo.local():
   335                 dest_repo.clone(src_repo, heads=revs, stream=stream)
   334                 destrepo.clone(srcrepo, heads=revs, stream=stream)
   336             elif src_repo.local():
   335             elif srcrepo.local():
   337                 src_repo.push(dest_repo, revs=revs)
   336                 srcrepo.push(destrepo, revs=revs)
   338             else:
   337             else:
   339                 raise util.Abort(_("clone from remote to remote not supported"))
   338                 raise util.Abort(_("clone from remote to remote not supported"))
   340 
   339 
   341         if dir_cleanup:
   340         if dircleanup:
   342             dir_cleanup.close()
   341             dircleanup.close()
   343 
   342 
   344         if dest_repo.local():
   343         if destrepo.local():
   345             fp = dest_repo.opener("hgrc", "w", text=True)
   344             fp = destrepo.opener("hgrc", "w", text=True)
   346             fp.write("[paths]\n")
   345             fp.write("[paths]\n")
   347             fp.write("default = %s\n" % abspath)
   346             fp.write("default = %s\n" % abspath)
   348             fp.close()
   347             fp.close()
   349 
   348 
   350             dest_repo.ui.setconfig('paths', 'default', abspath)
   349             destrepo.ui.setconfig('paths', 'default', abspath)
   351 
   350 
   352             if update:
   351             if update:
   353                 if update is not True:
   352                 if update is not True:
   354                     checkout = update
   353                     checkout = update
   355                     if src_repo.local():
   354                     if srcrepo.local():
   356                         checkout = src_repo.lookup(update)
   355                         checkout = srcrepo.lookup(update)
   357                 for test in (checkout, 'default', 'tip'):
   356                 for test in (checkout, 'default', 'tip'):
   358                     if test is None:
   357                     if test is None:
   359                         continue
   358                         continue
   360                     try:
   359                     try:
   361                         uprev = dest_repo.lookup(test)
   360                         uprev = destrepo.lookup(test)
   362                         break
   361                         break
   363                     except error.RepoLookupError:
   362                     except error.RepoLookupError:
   364                         continue
   363                         continue
   365                 bn = dest_repo[uprev].branch()
   364                 bn = destrepo[uprev].branch()
   366                 dest_repo.ui.status(_("updating to branch %s\n") % bn)
   365                 destrepo.ui.status(_("updating to branch %s\n") % bn)
   367                 _update(dest_repo, uprev)
   366                 _update(destrepo, uprev)
   368 
   367 
   369         return src_repo, dest_repo
   368         # clone all bookmarks
       
   369         if destrepo.local() and srcrepo.capable("pushkey"):
       
   370             rb = srcrepo.listkeys('bookmarks')
       
   371             for k, n in rb.iteritems():
       
   372                 try:
       
   373                     m = destrepo.lookup(n)
       
   374                     destrepo._bookmarks[k] = m
       
   375                 except error.RepoLookupError:
       
   376                     pass
       
   377             if rb:
       
   378                 bookmarks.write(destrepo)
       
   379         elif srcrepo.local() and destrepo.capable("pushkey"):
       
   380             for k, n in srcrepo._bookmarks.iteritems():
       
   381                 destrepo.pushkey('bookmarks', k, '', hex(n))
       
   382 
       
   383         return srcrepo, destrepo
   370     finally:
   384     finally:
   371         release(src_lock, dest_lock)
   385         release(srclock, destlock)
   372         if dir_cleanup is not None:
   386         if dircleanup is not None:
   373             dir_cleanup.cleanup()
   387             dircleanup.cleanup()
   374 
   388 
   375 def _showstats(repo, stats):
   389 def _showstats(repo, stats):
   376     repo.ui.status(_("%d files updated, %d files merged, "
   390     repo.ui.status(_("%d files updated, %d files merged, "
   377                      "%d files removed, %d files unresolved\n") % stats)
   391                      "%d files removed, %d files unresolved\n") % stats)
   378 
   392 
   413     displaychlist gets called with
   427     displaychlist gets called with
   414         (remoterepo, incomingchangesetlist, displayer) parameters,
   428         (remoterepo, incomingchangesetlist, displayer) parameters,
   415     and is supposed to contain only code that can't be unified.
   429     and is supposed to contain only code that can't be unified.
   416     """
   430     """
   417     source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
   431     source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
   418     other = repository(remoteui(repo, opts), source)
   432     other = peer(repo, opts, source)
   419     ui.status(_('comparing with %s\n') % url.hidepassword(source))
   433     ui.status(_('comparing with %s\n') % util.hidepassword(source))
   420     revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
   434     revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
   421 
   435 
   422     if revs:
   436     if revs:
   423         revs = [other.lookup(rev) for rev in revs]
   437         revs = [other.lookup(rev) for rev in revs]
   424     other, incoming, bundle = bundlerepo.getremotechanges(ui, repo, other, revs,
   438     other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
   425                                 opts["bundle"], opts["force"])
   439                                 revs, opts["bundle"], opts["force"])
   426     if incoming is None:
       
   427         ui.status(_("no changes found\n"))
       
   428         return subreporecurse()
       
   429 
       
   430     try:
   440     try:
   431         chlist = other.changelog.nodesbetween(incoming, revs)[0]
   441         if not chlist:
       
   442             ui.status(_("no changes found\n"))
       
   443             return subreporecurse()
       
   444 
   432         displayer = cmdutil.show_changeset(ui, other, opts, buffered)
   445         displayer = cmdutil.show_changeset(ui, other, opts, buffered)
   433 
   446 
   434         # XXX once graphlog extension makes it into core,
   447         # XXX once graphlog extension makes it into core,
   435         # should be replaced by a if graph/else
   448         # should be replaced by a if graph/else
   436         displaychlist(other, chlist, displayer)
   449         displaychlist(other, chlist, displayer)
   437 
   450 
   438         displayer.close()
   451         displayer.close()
   439     finally:
   452     finally:
   440         if hasattr(other, 'close'):
   453         cleanupfn()
   441             other.close()
       
   442         if bundle:
       
   443             os.unlink(bundle)
       
   444     subreporecurse()
   454     subreporecurse()
   445     return 0 # exit code is zero since we found incoming changes
   455     return 0 # exit code is zero since we found incoming changes
   446 
   456 
   447 def incoming(ui, repo, source, opts):
   457 def incoming(ui, repo, source, opts):
   448     def subreporecurse():
   458     def subreporecurse():
   470     return _incoming(display, subreporecurse, ui, repo, source, opts)
   480     return _incoming(display, subreporecurse, ui, repo, source, opts)
   471 
   481 
   472 def _outgoing(ui, repo, dest, opts):
   482 def _outgoing(ui, repo, dest, opts):
   473     dest = ui.expandpath(dest or 'default-push', dest or 'default')
   483     dest = ui.expandpath(dest or 'default-push', dest or 'default')
   474     dest, branches = parseurl(dest, opts.get('branch'))
   484     dest, branches = parseurl(dest, opts.get('branch'))
   475     ui.status(_('comparing with %s\n') % url.hidepassword(dest))
   485     ui.status(_('comparing with %s\n') % util.hidepassword(dest))
   476     revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
   486     revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
   477     if revs:
   487     if revs:
   478         revs = [repo.lookup(rev) for rev in revs]
   488         revs = [repo.lookup(rev) for rev in revs]
   479 
   489 
   480     other = repository(remoteui(repo, opts), dest)
   490     other = peer(repo, opts, dest)
   481     o = discovery.findoutgoing(repo, other, force=opts.get('force'))
   491     common, outheads = discovery.findcommonoutgoing(repo, other, revs,
       
   492                                                     force=opts.get('force'))
       
   493     o = repo.changelog.findmissing(common, outheads)
   482     if not o:
   494     if not o:
   483         ui.status(_("no changes found\n"))
   495         ui.status(_("no changes found\n"))
   484         return None
   496         return None
   485 
   497     return o
   486     return repo.changelog.nodesbetween(o, revs)[0]
       
   487 
   498 
   488 def outgoing(ui, repo, dest, opts):
   499 def outgoing(ui, repo, dest, opts):
   489     def recurse():
   500     def recurse():
   490         ret = 1
   501         ret = 1
   491         if opts.get('subrepos'):
   502         if opts.get('subrepos'):