def _pullapplyphases(pullop, remotephases):
    """apply phase movement from observed remote state"""
    if 'phases' in pullop.stepsdone:
        return
    pullop.stepsdone.add('phases')
    publishing = bool(remotephases.get('publishing', False))
    if remotephases and not publishing:
        # remote is new and unpublishing
        pheads, _dr = phases.analyzeremotephases(pullop.repo,
                                                 pullop.pulledsubset,
                                                 remotephases)
        dheads = pullop.pulledsubset
    else:
        # Remote is old or publishing all common changesets
        # should be seen as public
        pheads = pullop.pulledsubset
        dheads = []
    unfi = pullop.repo.unfiltered()
    phase = unfi._phasecache.phase
    rev = unfi.changelog.nodemap.get
    public = phases.public
    draft = phases.draft

    # exclude changesets already public locally and update the others
    pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
    if pheads:
        tr = pullop.gettransaction()
        phases.advanceboundary(pullop.repo, tr, public, pheads)

    # exclude changesets already draft locally and update the others
    dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
    if dheads:
        tr = pullop.gettransaction()
        phases.advanceboundary(pullop.repo, tr, draft, dheads)
Exemple #2
0
def _pullapplyphases(pullop, remotephases):
    """apply phase movement from observed remote state"""
    if 'phases' in pullop.stepsdone:
        return
    pullop.stepsdone.add('phases')
    publishing = bool(remotephases.get('publishing', False))
    if remotephases and not publishing:
        # remote is new and unpublishing
        pheads, _dr = phases.analyzeremotephases(pullop.repo,
                                                 pullop.pulledsubset,
                                                 remotephases)
        dheads = pullop.pulledsubset
    else:
        # Remote is old or publishing all common changesets
        # should be seen as public
        pheads = pullop.pulledsubset
        dheads = []
    unfi = pullop.repo.unfiltered()
    phase = unfi._phasecache.phase
    rev = unfi.changelog.nodemap.get
    public = phases.public
    draft = phases.draft

    # exclude changesets already public locally and update the others
    pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
    if pheads:
        tr = pullop.gettransaction()
        phases.advanceboundary(pullop.repo, tr, public, pheads)

    # exclude changesets already draft locally and update the others
    dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
    if dheads:
        tr = pullop.gettransaction()
        phases.advanceboundary(pullop.repo, tr, draft, dheads)
Exemple #3
0
def _localphasemove(pushop, nodes, phase=phases.public):
    """move <nodes> to <phase> in the local source repo"""
    if pushop.locallocked:
        phases.advanceboundary(pushop.repo, phase, nodes)
    else:
        # repo is not locked, do not change any phases!
        # Informs the user that phases should have been moved when
        # applicable.
        actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
        phasestr = phases.phasenames[phase]
        if actualmoves:
            pushop.ui.status(_('cannot lock source repo, skipping '
                               'local %s phase update\n') % phasestr)
def _pullphase(pullop):
    # Get remote phases data from remote
    pullop.todosteps.remove('phases')
    remotephases = pullop.remote.listkeys('phases')
    publishing = bool(remotephases.get('publishing', False))
    if remotephases and not publishing:
        # remote is new and unpublishing
        pheads, _dr = phases.analyzeremotephases(pullop.repo,
                                                 pullop.pulledsubset,
                                                 remotephases)
        phases.advanceboundary(pullop.repo, phases.public, pheads)
        phases.advanceboundary(pullop.repo, phases.draft, pullop.pulledsubset)
    else:
        # Remote is old or publishing all common changesets
        # should be seen as public
        phases.advanceboundary(pullop.repo, phases.public, pullop.pulledsubset)
Exemple #5
0
def _pullapplyphases(pullop, remotephases):
    """apply phase movement from observed remote state"""
    pullop.todosteps.remove('phases')
    publishing = bool(remotephases.get('publishing', False))
    if remotephases and not publishing:
        # remote is new and unpublishing
        pheads, _dr = phases.analyzeremotephases(pullop.repo,
                                                 pullop.pulledsubset,
                                                 remotephases)
        phases.advanceboundary(pullop.repo, phases.public, pheads)
        phases.advanceboundary(pullop.repo, phases.draft,
                               pullop.pulledsubset)
    else:
        # Remote is old or publishing all common changesets
        # should be seen as public
        phases.advanceboundary(pullop.repo, phases.public,
                               pullop.pulledsubset)
def _pullphase(pullop):
    # Get remote phases data from remote
    pullop.todosteps.remove('phases')
    remotephases = pullop.remote.listkeys('phases')
    publishing = bool(remotephases.get('publishing', False))
    if remotephases and not publishing:
        # remote is new and unpublishing
        pheads, _dr = phases.analyzeremotephases(pullop.repo,
                                                 pullop.pulledsubset,
                                                 remotephases)
        phases.advanceboundary(pullop.repo, phases.public, pheads)
        phases.advanceboundary(pullop.repo, phases.draft,
                               pullop.pulledsubset)
    else:
        # Remote is old or publishing all common changesets
        # should be seen as public
        phases.advanceboundary(pullop.repo, phases.public,
                               pullop.pulledsubset)
Exemple #7
0
def addchangegroup(repo,
                   source,
                   srctype,
                   url,
                   emptyok=False,
                   targetphase=phases.draft,
                   expectedtotal=None):
    """Add the changegroup returned by source.read() to this repo.
    srctype is a string like 'push', 'pull', or 'unbundle'.  url is
    the URL of the repo where this changegroup is coming from.

    Return an integer summarizing the change to this repo:
    - nothing changed or no source: 0
    - more heads than before: 1+added heads (2..n)
    - fewer heads than before: -1-removed heads (-2..-n)
    - number of heads stays the same: 1
    """
    repo = repo.unfiltered()

    def csmap(x):
        repo.ui.debug("add changeset %s\n" % short(x))
        return len(cl)

    def revmap(x):
        return cl.rev(x)

    if not source:
        return 0

    changesets = files = revisions = 0
    efiles = set()

    tr = repo.transaction("\n".join([srctype, util.hidepassword(url)]))
    # The transaction could have been created before and already carries source
    # information. In this case we use the top level data. We overwrite the
    # argument because we need to use the top level value (if they exist) in
    # this function.
    srctype = tr.hookargs.setdefault('source', srctype)
    url = tr.hookargs.setdefault('url', url)

    # write changelog data to temp files so concurrent readers will not see
    # inconsistent view
    cl = repo.changelog
    cl.delayupdate(tr)
    oldheads = cl.heads()
    try:
        repo.hook('prechangegroup', throw=True, **tr.hookargs)

        trp = weakref.proxy(tr)
        # pull off the changeset group
        repo.ui.status(_("adding changesets\n"))
        clstart = len(cl)

        class prog(object):
            def __init__(self, step, total):
                self._step = step
                self._total = total
                self._count = 1

            def __call__(self):
                repo.ui.progress(self._step,
                                 self._count,
                                 unit=_('chunks'),
                                 total=self._total)
                self._count += 1

        source.callback = prog(_('changesets'), expectedtotal)

        source.changelogheader()
        srccontent = cl.addgroup(source, csmap, trp)
        if not (srccontent or emptyok):
            raise util.Abort(_("received changelog group is empty"))
        clend = len(cl)
        changesets = clend - clstart
        for c in xrange(clstart, clend):
            efiles.update(repo[c].files())
        efiles = len(efiles)
        repo.ui.progress(_('changesets'), None)

        # pull off the manifest group
        repo.ui.status(_("adding manifests\n"))
        # manifests <= changesets
        source.callback = prog(_('manifests'), changesets)
        # no need to check for empty manifest group here:
        # if the result of the merge of 1 and 2 is the same in 3 and 4,
        # no new manifest will be created and the manifest group will
        # be empty during the pull
        source.manifestheader()
        repo.manifest.addgroup(source, revmap, trp)
        repo.ui.progress(_('manifests'), None)

        needfiles = {}
        if repo.ui.configbool('server', 'validate', default=False):
            # validate incoming csets have their manifests
            for cset in xrange(clstart, clend):
                mfnode = repo.changelog.read(repo.changelog.node(cset))[0]
                mfest = repo.manifest.readdelta(mfnode)
                # store file nodes we must see
                for f, n in mfest.iteritems():
                    needfiles.setdefault(f, set()).add(n)

        # process the files
        repo.ui.status(_("adding file changes\n"))
        source.callback = None
        pr = prog(_('files'), efiles)
        newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr,
                                                needfiles)
        revisions += newrevs
        files += newfiles

        dh = 0
        if oldheads:
            heads = cl.heads()
            dh = len(heads) - len(oldheads)
            for h in heads:
                if h not in oldheads and repo[h].closesbranch():
                    dh -= 1
        htext = ""
        if dh:
            htext = _(" (%+d heads)") % dh

        repo.ui.status(
            _("added %d changesets"
              " with %d changes to %d files%s\n") %
            (changesets, revisions, files, htext))
        repo.invalidatevolatilesets()

        if changesets > 0:
            p = lambda: tr.writepending() and repo.root or ""
            if 'node' not in tr.hookargs:
                tr.hookargs['node'] = hex(cl.node(clstart))
                hookargs = dict(tr.hookargs)
            else:
                hookargs = dict(tr.hookargs)
                hookargs['node'] = hex(cl.node(clstart))
            repo.hook('pretxnchangegroup', throw=True, pending=p, **hookargs)

        added = [cl.node(r) for r in xrange(clstart, clend)]
        publishing = repo.publishing()
        if srctype in ('push', 'serve'):
            # Old servers can not push the boundary themselves.
            # New servers won't push the boundary if changeset already
            # exists locally as secret
            #
            # We should not use added here but the list of all change in
            # the bundle
            if publishing:
                phases.advanceboundary(repo, tr, phases.public, srccontent)
            else:
                # Those changesets have been pushed from the outside, their
                # phases are going to be pushed alongside. Therefor
                # `targetphase` is ignored.
                phases.advanceboundary(repo, tr, phases.draft, srccontent)
                phases.retractboundary(repo, tr, phases.draft, added)
        elif srctype != 'strip':
            # publishing only alter behavior during push
            #
            # strip should not touch boundary at all
            phases.retractboundary(repo, tr, targetphase, added)

        if changesets > 0:
            if srctype != 'strip':
                # During strip, branchcache is invalid but coming call to
                # `destroyed` will repair it.
                # In other case we can safely update cache on disk.
                branchmap.updatecache(repo.filtered('served'))

            def runhooks():
                # These hooks run when the lock releases, not when the
                # transaction closes. So it's possible for the changelog
                # to have changed since we last saw it.
                if clstart >= len(repo):
                    return

                # forcefully update the on-disk branch cache
                repo.ui.debug("updating the branch cache\n")
                repo.hook("changegroup", **hookargs)

                for n in added:
                    args = hookargs.copy()
                    args['node'] = hex(n)
                    repo.hook("incoming", **args)

                newheads = [h for h in repo.heads() if h not in oldheads]
                repo.ui.log("incoming",
                            "%s incoming changes - new heads: %s\n",
                            len(added),
                            ', '.join([hex(c[:6]) for c in newheads]))

            tr.addpostclose('changegroup-runhooks-%020i' % clstart,
                            lambda tr: repo._afterlock(runhooks))

        tr.close()

    finally:
        tr.release()
        repo.ui.flush()
    # never return 0 here:
    if dh < 0:
        return dh - 1
    else:
        return dh + 1
def addchangegroup(repo, source, srctype, url, emptyok=False):
    """Add the changegroup returned by source.read() to this repo.
    srctype is a string like 'push', 'pull', or 'unbundle'.  url is
    the URL of the repo where this changegroup is coming from.

    Return an integer summarizing the change to this repo:
    - nothing changed or no source: 0
    - more heads than before: 1+added heads (2..n)
    - fewer heads than before: -1-removed heads (-2..-n)
    - number of heads stays the same: 1
    """
    repo = repo.unfiltered()
    def csmap(x):
        repo.ui.debug("add changeset %s\n" % short(x))
        return len(cl)

    def revmap(x):
        return cl.rev(x)

    if not source:
        return 0

    repo.hook('prechangegroup', throw=True, source=srctype, url=url)

    changesets = files = revisions = 0
    efiles = set()

    # write changelog data to temp files so concurrent readers will not see
    # inconsistent view
    cl = repo.changelog
    cl.delayupdate()
    oldheads = cl.heads()

    tr = repo.transaction("\n".join([srctype, util.hidepassword(url)]))
    try:
        trp = weakref.proxy(tr)
        # pull off the changeset group
        repo.ui.status(_("adding changesets\n"))
        clstart = len(cl)
        class prog(object):
            step = _('changesets')
            count = 1
            ui = repo.ui
            total = None
            def __call__(repo):
                repo.ui.progress(repo.step, repo.count, unit=_('chunks'),
                                 total=repo.total)
                repo.count += 1
        pr = prog()
        source.callback = pr

        source.changelogheader()
        srccontent = cl.addgroup(source, csmap, trp)
        if not (srccontent or emptyok):
            raise util.Abort(_("received changelog group is empty"))
        clend = len(cl)
        changesets = clend - clstart
        for c in xrange(clstart, clend):
            efiles.update(repo[c].files())
        efiles = len(efiles)
        repo.ui.progress(_('changesets'), None)

        # pull off the manifest group
        repo.ui.status(_("adding manifests\n"))
        pr.step = _('manifests')
        pr.count = 1
        pr.total = changesets # manifests <= changesets
        # no need to check for empty manifest group here:
        # if the result of the merge of 1 and 2 is the same in 3 and 4,
        # no new manifest will be created and the manifest group will
        # be empty during the pull
        source.manifestheader()
        repo.manifest.addgroup(source, revmap, trp)
        repo.ui.progress(_('manifests'), None)

        needfiles = {}
        if repo.ui.configbool('server', 'validate', default=False):
            # validate incoming csets have their manifests
            for cset in xrange(clstart, clend):
                mfest = repo.changelog.read(repo.changelog.node(cset))[0]
                mfest = repo.manifest.readdelta(mfest)
                # store file nodes we must see
                for f, n in mfest.iteritems():
                    needfiles.setdefault(f, set()).add(n)

        # process the files
        repo.ui.status(_("adding file changes\n"))
        pr.step = _('files')
        pr.count = 1
        pr.total = efiles
        source.callback = None

        newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr,
                                                needfiles)
        revisions += newrevs
        files += newfiles

        dh = 0
        if oldheads:
            heads = cl.heads()
            dh = len(heads) - len(oldheads)
            for h in heads:
                if h not in oldheads and repo[h].closesbranch():
                    dh -= 1
        htext = ""
        if dh:
            htext = _(" (%+d heads)") % dh

        repo.ui.status(_("added %d changesets"
                         " with %d changes to %d files%s\n")
                         % (changesets, revisions, files, htext))
        repo.invalidatevolatilesets()

        if changesets > 0:
            p = lambda: cl.writepending() and repo.root or ""
            if 'node' not in tr.hookargs:
                tr.hookargs['node'] = hex(cl.node(clstart))
            repo.hook('pretxnchangegroup', throw=True, source=srctype,
                      url=url, pending=p, **tr.hookargs)

        added = [cl.node(r) for r in xrange(clstart, clend)]
        publishing = repo.ui.configbool('phases', 'publish', True)
        if srctype in ('push', 'serve'):
            # Old servers can not push the boundary themselves.
            # New servers won't push the boundary if changeset already
            # exists locally as secret
            #
            # We should not use added here but the list of all change in
            # the bundle
            if publishing:
                phases.advanceboundary(repo, phases.public, srccontent)
            else:
                phases.advanceboundary(repo, phases.draft, srccontent)
                phases.retractboundary(repo, phases.draft, added)
        elif srctype != 'strip':
            # publishing only alter behavior during push
            #
            # strip should not touch boundary at all
            phases.retractboundary(repo, phases.draft, added)

        # make changelog see real files again
        cl.finalize(trp)

        tr.close()

        if changesets > 0:
            if srctype != 'strip':
                # During strip, branchcache is invalid but coming call to
                # `destroyed` will repair it.
                # In other case we can safely update cache on disk.
                branchmap.updatecache(repo.filtered('served'))
            def runhooks():
                # These hooks run when the lock releases, not when the
                # transaction closes. So it's possible for the changelog
                # to have changed since we last saw it.
                if clstart >= len(repo):
                    return

                # forcefully update the on-disk branch cache
                repo.ui.debug("updating the branch cache\n")
                repo.hook("changegroup", source=srctype, url=url,
                          **tr.hookargs)

                for n in added:
                    repo.hook("incoming", node=hex(n), source=srctype,
                              url=url)

                newheads = [h for h in repo.heads() if h not in oldheads]
                repo.ui.log("incoming",
                            "%s incoming changes - new heads: %s\n",
                            len(added),
                            ', '.join([hex(c[:6]) for c in newheads]))
            repo._afterlock(runhooks)

    finally:
        tr.release()
    # never return 0 here:
    if dh < 0:
        return dh - 1
    else:
        return dh + 1