mercurial/exchange.py
changeset 20346 42df1fe32552
parent 20345 8567b4ea76ac
child 20347 3ec5f833348e
equal deleted inserted replaced
20345:8567b4ea76ac 20346:42df1fe32552
     8 from i18n import _
     8 from i18n import _
     9 from node import hex
     9 from node import hex
    10 import errno
    10 import errno
    11 import util, scmutil, changegroup
    11 import util, scmutil, changegroup
    12 import discovery, phases, obsolete, bookmarks
    12 import discovery, phases, obsolete, bookmarks
       
    13 
       
    14 
       
    15 class pushoperation(object):
       
    16     """A object that represent a single push operation
       
    17 
       
    18     It purpose is to carry push related state and very common operation.
       
    19 
       
    20     A new should be created at the begining of each push and discarded
       
    21     afterward.
       
    22     """
       
    23 
       
    24     def __init__(self, repo):
       
    25         # repo we push from
       
    26         self.repo = repo
    13 
    27 
    14 def push(repo, remote, force=False, revs=None, newbranch=False):
    28 def push(repo, remote, force=False, revs=None, newbranch=False):
    15     '''Push outgoing changesets (limited by revs) from a local
    29     '''Push outgoing changesets (limited by revs) from a local
    16     repository to remote. Return an integer:
    30     repository to remote. Return an integer:
    17       - None means nothing to push
    31       - None means nothing to push
    18       - 0 means HTTP error
    32       - 0 means HTTP error
    19       - 1 means we pushed and remote head count is unchanged *or*
    33       - 1 means we pushed and remote head count is unchanged *or*
    20         we have outgoing changesets but refused to push
    34         we have outgoing changesets but refused to push
    21       - other values as described by addchangegroup()
    35       - other values as described by addchangegroup()
    22     '''
    36     '''
       
    37     pushop = pushoperation(repo)
    23     if remote.local():
    38     if remote.local():
    24         missing = set(repo.requirements) - remote.local().supported
    39         missing = set(pushop.repo.requirements) - remote.local().supported
    25         if missing:
    40         if missing:
    26             msg = _("required features are not"
    41             msg = _("required features are not"
    27                     " supported in the destination:"
    42                     " supported in the destination:"
    28                     " %s") % (', '.join(sorted(missing)))
    43                     " %s") % (', '.join(sorted(missing)))
    29             raise util.Abort(msg)
    44             raise util.Abort(msg)
    36     # unbundle assumes local user cannot lock remote repo (new ssh
    51     # unbundle assumes local user cannot lock remote repo (new ssh
    37     # servers, http servers).
    52     # servers, http servers).
    38 
    53 
    39     if not remote.canpush():
    54     if not remote.canpush():
    40         raise util.Abort(_("destination does not support push"))
    55         raise util.Abort(_("destination does not support push"))
    41     unfi = repo.unfiltered()
    56     unfi = pushop.repo.unfiltered()
    42     def localphasemove(nodes, phase=phases.public):
    57     def localphasemove(nodes, phase=phases.public):
    43         """move <nodes> to <phase> in the local source repo"""
    58         """move <nodes> to <phase> in the local source repo"""
    44         if locallock is not None:
    59         if locallock is not None:
    45             phases.advanceboundary(repo, phase, nodes)
    60             phases.advanceboundary(pushop.repo, phase, nodes)
    46         else:
    61         else:
    47             # repo is not locked, do not change any phases!
    62             # repo is not locked, do not change any phases!
    48             # Informs the user that phases should have been moved when
    63             # Informs the user that phases should have been moved when
    49             # applicable.
    64             # applicable.
    50             actualmoves = [n for n in nodes if phase < repo[n].phase()]
    65             actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
    51             phasestr = phases.phasenames[phase]
    66             phasestr = phases.phasenames[phase]
    52             if actualmoves:
    67             if actualmoves:
    53                 repo.ui.status(_('cannot lock source repo, skipping local'
    68                 pushop.repo.ui.status(_('cannot lock source repo, skipping '
    54                                  ' %s phase update\n') % phasestr)
    69                                         'local %s phase update\n') % phasestr)
    55     # get local lock as we might write phase data
    70     # get local lock as we might write phase data
    56     locallock = None
    71     locallock = None
    57     try:
    72     try:
    58         locallock = repo.lock()
    73         locallock = pushop.repo.lock()
    59     except IOError, err:
    74     except IOError, err:
    60         if err.errno != errno.EACCES:
    75         if err.errno != errno.EACCES:
    61             raise
    76             raise
    62         # source repo cannot be locked.
    77         # source repo cannot be locked.
    63         # We do not abort the push, but just disable the local phase
    78         # We do not abort the push, but just disable the local phase
    64         # synchronisation.
    79         # synchronisation.
    65         msg = 'cannot lock source repository: %s\n' % err
    80         msg = 'cannot lock source repository: %s\n' % err
    66         repo.ui.debug(msg)
    81         pushop.repo.ui.debug(msg)
    67     try:
    82     try:
    68         repo.checkpush(force, revs)
    83         pushop.repo.checkpush(force, revs)
    69         lock = None
    84         lock = None
    70         unbundle = remote.capable('unbundle')
    85         unbundle = remote.capable('unbundle')
    71         if not unbundle:
    86         if not unbundle:
    72             lock = remote.lock()
    87             lock = remote.lock()
    73         try:
    88         try:
   107                                 raise util.Abort(mso % ctx)
   122                                 raise util.Abort(mso % ctx)
   108                             elif ctx.troubled():
   123                             elif ctx.troubled():
   109                                 raise util.Abort(_(mst)
   124                                 raise util.Abort(_(mst)
   110                                                  % (ctx.troubles()[0],
   125                                                  % (ctx.troubles()[0],
   111                                                     ctx))
   126                                                     ctx))
   112                     newbm = repo.ui.configlist('bookmarks', 'pushing')
   127                     newbm = pushop.repo.ui.configlist('bookmarks', 'pushing')
   113                     discovery.checkheads(unfi, remote, outgoing,
   128                     discovery.checkheads(unfi, remote, outgoing,
   114                                          remoteheads, newbranch,
   129                                          remoteheads, newbranch,
   115                                          bool(inc), newbm)
   130                                          bool(inc), newbm)
   116 
   131 
   117                 # TODO: get bundlecaps from remote
   132                 # TODO: get bundlecaps from remote
   118                 bundlecaps = None
   133                 bundlecaps = None
   119                 # create a changegroup from local
   134                 # create a changegroup from local
   120                 if revs is None and not (outgoing.excluded
   135                 if revs is None and not (outgoing.excluded
   121                                          or repo.changelog.filteredrevs):
   136                                         or pushop.repo.changelog.filteredrevs):
   122                     # push everything,
   137                     # push everything,
   123                     # use the fast path, no race possible on push
   138                     # use the fast path, no race possible on push
   124                     bundler = changegroup.bundle10(repo, bundlecaps)
   139                     bundler = changegroup.bundle10(pushop.repo, bundlecaps)
   125                     cg = repo._changegroupsubset(outgoing,
   140                     cg = pushop.repo._changegroupsubset(outgoing,
   126                                                  bundler,
   141                                                         bundler,
   127                                                  'push',
   142                                                         'push',
   128                                                  fastpath=True)
   143                                                         fastpath=True)
   129                 else:
   144                 else:
   130                     cg = repo.getlocalbundle('push', outgoing, bundlecaps)
   145                     cg = pushop.repo.getlocalbundle('push', outgoing,
       
   146                                                     bundlecaps)
   131 
   147 
   132                 # apply changegroup to remote
   148                 # apply changegroup to remote
   133                 if unbundle:
   149                 if unbundle:
   134                     # local repo finds heads on server, finds out what
   150                     # local repo finds heads on server, finds out what
   135                     # revs it must push. once revs transferred, if server
   151                     # revs it must push. once revs transferred, if server
   141                     # http: return remote's addchangegroup() or 0 for error
   157                     # http: return remote's addchangegroup() or 0 for error
   142                     ret = remote.unbundle(cg, remoteheads, 'push')
   158                     ret = remote.unbundle(cg, remoteheads, 'push')
   143                 else:
   159                 else:
   144                     # we return an integer indicating remote head count
   160                     # we return an integer indicating remote head count
   145                     # change
   161                     # change
   146                     ret = remote.addchangegroup(cg, 'push', repo.url())
   162                     ret = remote.addchangegroup(cg, 'push', pushop.repo.url())
   147 
   163 
   148             if ret:
   164             if ret:
   149                 # push succeed, synchronize target of the push
   165                 # push succeed, synchronize target of the push
   150                 cheads = outgoing.missingheads
   166                 cheads = outgoing.missingheads
   151             elif revs is None:
   167             elif revs is None:
   165                 #     missing = ((commonheads::missingheads) - commonheads)
   181                 #     missing = ((commonheads::missingheads) - commonheads)
   166                 #
   182                 #
   167                 # We can pick:
   183                 # We can pick:
   168                 # * missingheads part of common (::commonheads)
   184                 # * missingheads part of common (::commonheads)
   169                 common = set(outgoing.common)
   185                 common = set(outgoing.common)
   170                 nm = repo.changelog.nodemap
   186                 nm = pushop.repo.changelog.nodemap
   171                 cheads = [node for node in revs if nm[node] in common]
   187                 cheads = [node for node in revs if nm[node] in common]
   172                 # and
   188                 # and
   173                 # * commonheads parents on missing
   189                 # * commonheads parents on missing
   174                 revset = unfi.set('%ln and parents(roots(%ln))',
   190                 revset = unfi.set('%ln and parents(roots(%ln))',
   175                                  outgoing.commonheads,
   191                                  outgoing.commonheads,
   176                                  outgoing.missing)
   192                                  outgoing.missing)
   177                 cheads.extend(c.node() for c in revset)
   193                 cheads.extend(c.node() for c in revset)
   178             # even when we don't push, exchanging phase data is useful
   194             # even when we don't push, exchanging phase data is useful
   179             remotephases = remote.listkeys('phases')
   195             remotephases = remote.listkeys('phases')
   180             if (repo.ui.configbool('ui', '_usedassubrepo', False)
   196             if (pushop.repo.ui.configbool('ui', '_usedassubrepo', False)
   181                 and remotephases    # server supports phases
   197                 and remotephases    # server supports phases
   182                 and ret is None # nothing was pushed
   198                 and ret is None # nothing was pushed
   183                 and remotephases.get('publishing', False)):
   199                 and remotephases.get('publishing', False)):
   184                 # When:
   200                 # When:
   185                 # - this is a subrepo push
   201                 # - this is a subrepo push
   193                 remotephases = {'publishing': 'True'}
   209                 remotephases = {'publishing': 'True'}
   194             if not remotephases: # old server or public only repo
   210             if not remotephases: # old server or public only repo
   195                 localphasemove(cheads)
   211                 localphasemove(cheads)
   196                 # don't push any phase data as there is nothing to push
   212                 # don't push any phase data as there is nothing to push
   197             else:
   213             else:
   198                 ana = phases.analyzeremotephases(repo, cheads, remotephases)
   214                 ana = phases.analyzeremotephases(pushop.repo, cheads,
       
   215                                                  remotephases)
   199                 pheads, droots = ana
   216                 pheads, droots = ana
   200                 ### Apply remote phase on local
   217                 ### Apply remote phase on local
   201                 if remotephases.get('publishing', False):
   218                 if remotephases.get('publishing', False):
   202                     localphasemove(cheads)
   219                     localphasemove(cheads)
   203                 else: # publish = False
   220                 else: # publish = False
   214                     r = remote.pushkey('phases',
   231                     r = remote.pushkey('phases',
   215                                        newremotehead.hex(),
   232                                        newremotehead.hex(),
   216                                        str(phases.draft),
   233                                        str(phases.draft),
   217                                        str(phases.public))
   234                                        str(phases.public))
   218                     if not r:
   235                     if not r:
   219                         repo.ui.warn(_('updating %s to public failed!\n')
   236                         pushop.repo.ui.warn(_('updating %s to public failed!\n')
   220                                         % newremotehead)
   237                                         % newremotehead)
   221             repo.ui.debug('try to push obsolete markers to remote\n')
   238             pushop.repo.ui.debug('try to push obsolete markers to remote\n')
   222             obsolete.syncpush(repo, remote)
   239             obsolete.syncpush(pushop.repo, remote)
   223         finally:
   240         finally:
   224             if lock is not None:
   241             if lock is not None:
   225                 lock.release()
   242                 lock.release()
   226     finally:
   243     finally:
   227         if locallock is not None:
   244         if locallock is not None:
   228             locallock.release()
   245             locallock.release()
   229 
   246 
   230     bookmarks.updateremote(repo.ui, unfi, remote, revs)
   247     bookmarks.updateremote(pushop.repo.ui, unfi, remote, revs)
   231     return ret
   248     return ret