Example #1
0
def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
    newmatch = narrowspec.match(repo.root, newincludes, newexcludes)

    # TODO(martinvonz): Get expansion working with widening/narrowing.
    if narrowspec.needsexpansion(newincludes):
        raise error.Abort('Expansion not yet supported on pull')

    def pullbundle2extraprepare_widen(orig, pullop, kwargs):
        orig(pullop, kwargs)
        # The old{in,ex}cludepats have already been set by orig()
        kwargs['includepats'] = newincludes
        kwargs['excludepats'] = newexcludes

    wrappedextraprepare = extensions.wrappedfunction(
        exchange, '_pullbundle2extraprepare', pullbundle2extraprepare_widen)

    # define a function that narrowbundle2 can call after creating the
    # backup bundle, but before applying the bundle from the server
    def setnewnarrowpats():
        repo.setnarrowpats(newincludes, newexcludes)

    repo.setnewnarrowpats = setnewnarrowpats

    ds = repo.dirstate
    p1, p2 = ds.p1(), ds.p2()
    with ds.parentchange():
        ds.setparents(node.nullid, node.nullid)
    common = commoninc[0]
    with wrappedextraprepare:
        exchange.pull(repo, remote, heads=common)
    with ds.parentchange():
        ds.setparents(p1, p2)

    actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
    addgaction = actions['g'].append

    mf = repo['.'].manifest().matches(newmatch)
    for f, fn in mf.iteritems():
        if f not in repo.dirstate:
            addgaction(
                (f, (mf.flags(f), False), "add from widened narrow clone"))

    merge.applyupdates(repo,
                       actions,
                       wctx=repo[None],
                       mctx=repo['.'],
                       overwrite=False)
    merge.recordupdates(repo, actions, branchmerge=False)
Example #2
0
def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
           newincludes, newexcludes):
    newmatch = narrowspec.match(repo.root, newincludes, newexcludes)

    # for now we assume that if a server has ellipses enabled, we will be
    # exchanging ellipses nodes. In future we should add ellipses as a client
    # side requirement (maybe) to distinguish a client is shallow or not and
    # then send that information to server whether we want ellipses or not.
    # Theoretically a non-ellipses repo should be able to use narrow
    # functionality from an ellipses enabled server
    ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()

    def pullbundle2extraprepare_widen(orig, pullop, kwargs):
        orig(pullop, kwargs)
        # The old{in,ex}cludepats have already been set by orig()
        kwargs['includepats'] = newincludes
        kwargs['excludepats'] = newexcludes
    wrappedextraprepare = extensions.wrappedfunction(exchange,
        '_pullbundle2extraprepare', pullbundle2extraprepare_widen)

    # define a function that narrowbundle2 can call after creating the
    # backup bundle, but before applying the bundle from the server
    def setnewnarrowpats():
        repo.setnarrowpats(newincludes, newexcludes)
    repo.setnewnarrowpats = setnewnarrowpats
    # silence the devel-warning of applying an empty changegroup
    overrides = {('devel', 'all-warnings'): False}

    with ui.uninterruptable():
        common = commoninc[0]
        if ellipsesremote:
            ds = repo.dirstate
            p1, p2 = ds.p1(), ds.p2()
            with ds.parentchange():
                ds.setparents(node.nullid, node.nullid)
            with wrappedextraprepare,\
                 repo.ui.configoverride(overrides, 'widen'):
                exchange.pull(repo, remote, heads=common)
            with ds.parentchange():
                ds.setparents(p1, p2)
        else:
            with remote.commandexecutor() as e:
                bundle = e.callcommand('narrow_widen', {
                    'oldincludes': oldincludes,
                    'oldexcludes': oldexcludes,
                    'newincludes': newincludes,
                    'newexcludes': newexcludes,
                    'cgversion': '03',
                    'commonheads': common,
                    'known': [],
                    'ellipses': False,
                }).result()

            with repo.transaction('widening') as tr,\
                 repo.ui.configoverride(overrides, 'widen'):
                tgetter = lambda: tr
                bundle2.processbundle(repo, bundle,
                        transactiongetter=tgetter)

        repo.setnewnarrowpats()
        actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
        addgaction = actions['g'].append

        mf = repo['.'].manifest().matches(newmatch)
        for f, fn in mf.iteritems():
            if f not in repo.dirstate:
                addgaction((f, (mf.flags(f), False),
                            "add from widened narrow clone"))

        merge.applyupdates(repo, actions, wctx=repo[None],
                           mctx=repo['.'], overwrite=False)
        merge.recordupdates(repo, actions, branchmerge=False)
Example #3
0
def hgupdate(repo, node, branchmerge, force, partial):
    """Slightly hacked mercurial.merge.update()
    (To see changes, diff it with version 1.1.2. Please keep this version number up-to-date
    if you change the derived version.)
    
    Perform a merge between the working directory and the given node
    
    node = the node to update to, or None if unspecified
    branchmerge = whether to merge between branches
    force = whether to force branch merging or file overwriting
    partial = a function to filter file lists (dirstate not updated)
    
    The table below shows all the behaviors of the update command
    given the -c and -C or no options, whether the working directory
    is dirty, whether a revision is specified, and the relationship of
    the parent rev to the target rev (linear, on the same named
    branch, or on another named branch).
    
    This logic is tested by test-update-branches.
    
    -c -C dirty rev | linear same cross
    n n n n | ok (1) x
    n n n y | ok ok ok
    n n y * | merge (2) (2)
    n y * * | --- discard ---
    y n y * | --- (3) ---
    y n n * | --- ok ---
    y y * * | --- (4) ---
    
    x = can't happen
    * = don't-care
    1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
    2 = abort: crosses branches (use 'hg merge' to merge or
    use 'hg update -C' to discard changes)
    3 = abort: uncommitted local changes
    4 = incompatible options (checked in commands.py)
    """

    wlock = repo.wlock()

    try:
        wc = repo[None]  ###working copy change context
        if node is None:  ###see nothing passing it node
            # tip of current branch
            try:
                node = repo.branchtags()[wc.branch()]
            except KeyError:
                if wc.branch() == "default":  # no default branch!
                    node = repo.lookup("tip")  # update to tip
                else:
                    raise util.Abort(_("branch %s not found") % wc.branch())
        overwrite = force and not branchmerge
        pl = wc.parents()  ###ParentList of working copy
        p1, p2 = pl[0], repo[node]  ### MyChangeContext, MergeChangeContet
        pa = p1.ancestor(p2)  ### CommonAncestorChangeContext
        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(
            p2)  ##changeset hashes & short hashes
        fastforward = False

        ### check phase
        if not overwrite and len(pl) > 1:  ### wc has multiple parents
            raise util.Abort(_("outstanding uncommitted merges"))
        if branchmerge:
            if pa == p2:  ###she is the common ancestor
                raise util.Abort(_("can't merge with ancestor"))
            elif pa == p1:  ###me is the common ancestor
                if p1.branch() != p2.branch(
                ):  ###...but we are different named branches (usually 'default')
                    fastforward = True  ### Merging a branch back into common ancestor
                else:
                    raise util.Abort(
                        _("nothing to merge (use 'hg update'"
                          " or check 'hg heads')"))
            if not force and (wc.files()
                              or wc.deleted()):  ### edits, adds, or mods in wc
                raise util.Abort(_("outstanding uncommitted changes"))
        elif not overwrite:
            if pa == p1 or pa == p2:  # linear
                pass  # all good
            elif p1.branch() == p2.branch():
                if wc.files() or wc.deleted():
                    raise util.Abort(
                        _("crosses branches (use 'hg merge' or "
                          "'hg update -C' to discard changes)"))
                raise util.Abort(
                    _("crosses branches (use 'hg merge' "
                      "or 'hg update -C')"))
            elif wc.files() or wc.deleted():
                raise util.Abort(
                    _("crosses named branches (use "
                      "'hg update -C' to discard changes)"))
            else:
                # Allow jumping branches if there are no changes
                overwrite = True

        ### calculate phase
        action = []
        if not force:
            merge._checkunknown(wc, p2)
        #~ if not util.checkcase(repo.path):
        #~ _checkcollision(p2)
        action += merge._forgetremoved(wc, p2, branchmerge)
        action += merge.manifestmerge(repo, wc, p2, pa, overwrite, partial)

        ### apply phase
        if not branchmerge:  # just jump to the new rev
            fp1, fp2, xp1, xp2 = fp2, mercurial.node.nullid, xp2, ''
        if not partial:
            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)

        stats = merge.applyupdates(repo, action, wc, p2, pa)

        #added: I want to record updates even if partial merge
        #merge.recordupdates(repo, action, branchmerge)

        if not partial:
            repo.dirstate.setparents(fp1, fp2)
            merge.recordupdates(repo, action, branchmerge)
            if not branchmerge and not fastforward:
                repo.dirstate.setbranch(p2.branch())

    finally:
        wlock.release()

    if not partial:
        repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
    return stats
Example #4
0
def merge_update(
    orig,
    repo,
    node,
    branchmerge,
    force,
    ancestor=None,
    mergeancestor=False,
    labels=None,
    matcher=None,
    mergeforce=False,
    updatecheck=None,
    wc=None,
):
    """Apparently node can be a 20-byte hash or an integer referencing a
    revision number.
    """
    assert node is not None

    if not util.safehasattr(repo.dirstate, "eden_client"):
        why_not_eden = "This is not an eden repository."
    elif matcher is not None and not matcher.always():
        why_not_eden = "We don't support doing a partial update through " "eden yet."
    elif branchmerge:
        # TODO: We potentially should support handling this scenario ourself in
        # the future.  For now we simply haven't investigated what the correct
        # semantics are in this case.
        why_not_eden = 'branchmerge is "truthy:" %s.' % branchmerge
    elif ancestor is not None:
        # TODO: We potentially should support handling this scenario ourself in
        # the future.  For now we simply haven't investigated what the correct
        # semantics are in this case.
        why_not_eden = "ancestor is not None: %s." % ancestor
    elif wc is not None and wc.isinmemory():
        # In memory merges do not operate on the working directory,
        # so we don't need to ask eden to change the working directory state
        # at all, and can use the vanilla merge logic in this case.
        why_not_eden = "merge is in-memory"
    else:
        # TODO: We probably also need to set why_not_eden if there are
        # subrepositories.  (Personally I might vote for just not supporting
        # subrepos in eden.)
        why_not_eden = None

    if why_not_eden:
        repo.ui.debug("falling back to non-eden update code path: %s\n" %
                      why_not_eden)
        return orig(
            repo,
            node,
            branchmerge,
            force,
            ancestor=ancestor,
            mergeancestor=mergeancestor,
            labels=labels,
            matcher=matcher,
            mergeforce=mergeforce,
            updatecheck=updatecheck,
            wc=wc,
        )
    else:
        repo.ui.debug("using eden update code path\n")

    with repo.wlock():
        wctx = repo[None]
        parents = wctx.parents()

        p1ctx = parents[0]
        destctx = repo[node]
        deststr = str(destctx)

        if not force:
            # Make sure there isn't an outstanding merge or unresolved files.
            if len(parents) > 1:
                raise error.Abort(_("outstanding uncommitted merge"))
            ms = mergemod.mergestate.read(repo)
            if list(ms.unresolved()):
                raise error.Abort(_("outstanding merge conflicts"))

            # The vanilla merge code disallows updating between two unrelated
            # branches if the working directory is dirty.  I don't really see a
            # good reason to disallow this; it should be treated the same as if
            # we committed the changes, checked out the other branch then tried
            # to graft the changes here.

            if p1ctx == destctx:
                # No update to perform.
                # Just invoke the hooks and return.
                repo.hook("preupdate", throw=True, parent1=deststr, parent2="")
                repo.hook("update", parent1=deststr, parent2="", error=0)
                return 0, 0, 0, 0

            # If we are in noconflict mode, then we must do a DRY_RUN first to
            # see if there are any conflicts that should prevent us from
            # attempting the update.
            if updatecheck == "noconflict":
                conflicts = repo.dirstate.eden_client.checkout(
                    destctx.node(), CheckoutMode.DRY_RUN)
                if conflicts:
                    actions = _determine_actions_for_conflicts(
                        repo, p1ctx, conflicts)
                    _check_actions_and_raise_if_there_are_conflicts(actions)

        # Invoke the preupdate hook
        repo.hook("preupdate", throw=True, parent1=deststr, parent2="")
        # Record that we're in the middle of an update
        try:
            vfs = repo.localvfs
        except AttributeError:
            vfs = repo.vfs
        vfs.write("updatestate", destctx.hex())

        # Ask eden to perform the checkout
        if force:
            # eden_client.checkout() returns the list of conflicts here,
            # but since this is a force update it will have already replaced
            # the conflicts with the destination file state, so we don't have
            # to do anything with them here.
            conflicts = repo.dirstate.eden_client.checkout(
                destctx.node(), CheckoutMode.FORCE)
            # We do still need to make sure to update the merge state though.
            # In the non-force code path the merge state is updated in
            # _handle_update_conflicts().
            ms = mergemod.mergestate.clean(repo, p1ctx.node(), destctx.node(),
                                           labels)
            ms.commit()

            stats = 0, 0, 0, 0
            actions = {}
        else:
            conflicts = repo.dirstate.eden_client.checkout(
                destctx.node(), CheckoutMode.NORMAL)
            # TODO(mbolin): Add a warning if we did a DRY_RUN and the conflicts
            # we get here do not match. Only in the event of a race would we
            # expect them to differ from when the DRY_RUN was done (or if we
            # decide that DIRECTORY_NOT_EMPTY conflicts do not need to be
            # reported during a DRY_RUN).

            stats, actions = _handle_update_conflicts(repo, wctx, p1ctx,
                                                      destctx, labels,
                                                      conflicts, force)

        with repo.dirstate.parentchange():
            if force:
                # If the user has done an `update --clean`, then we should
                # remove all entries from the dirstate. Note this call to
                # clear() will also remove the parents, but we set them on the
                # next line, so we'll be OK.
                repo.dirstate.clear()
            # TODO(mbolin): Set the second parent, if appropriate.
            repo.setparents(destctx.node())
            mergemod.recordupdates(repo, actions, branchmerge)

            # Clear the update state
            util.unlink(vfs.join("updatestate"))

    # Invoke the update hook
    repo.hook("update", parent1=deststr, parent2="", error=stats[3])

    return stats
Example #5
0
def hgupdate(repo, node, branchmerge, force, partial):
    """Slightly hacked mercurial.merge.update()
    (To see changes, diff it with version 1.1.2. Please keep this version number up-to-date
    if you change the derived version.)
    
    Perform a merge between the working directory and the given node
    
    node = the node to update to, or None if unspecified
    branchmerge = whether to merge between branches
    force = whether to force branch merging or file overwriting
    partial = a function to filter file lists (dirstate not updated)
    
    The table below shows all the behaviors of the update command
    given the -c and -C or no options, whether the working directory
    is dirty, whether a revision is specified, and the relationship of
    the parent rev to the target rev (linear, on the same named
    branch, or on another named branch).
    
    This logic is tested by test-update-branches.
    
    -c -C dirty rev | linear same cross
    n n n n | ok (1) x
    n n n y | ok ok ok
    n n y * | merge (2) (2)
    n y * * | --- discard ---
    y n y * | --- (3) ---
    y n n * | --- ok ---
    y y * * | --- (4) ---
    
    x = can't happen
    * = don't-care
    1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
    2 = abort: crosses branches (use 'hg merge' to merge or
    use 'hg update -C' to discard changes)
    3 = abort: uncommitted local changes
    4 = incompatible options (checked in commands.py)
    """ 

    wlock = repo.wlock()
    
    try:
        wc = repo[None] ###working copy change context
        if node is None: ###see nothing passing it node
            # tip of current branch
            try:
                node = repo.branchtags()[wc.branch()]
            except KeyError:
                if wc.branch() == "default": # no default branch!
                    node = repo.lookup("tip") # update to tip
                else:
                    raise util.Abort(_("branch %s not found") % wc.branch())
        overwrite = force and not branchmerge
        pl = wc.parents() ###ParentList of working copy
        p1, p2 = pl[0], repo[node] ### MyChangeContext, MergeChangeContet
        pa = p1.ancestor(p2) ### CommonAncestorChangeContext
        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) ##changeset hashes & short hashes
        fastforward = False

        ### check phase
        if not overwrite and len(pl) > 1: ### wc has multiple parents
            raise util.Abort(_("outstanding uncommitted merges"))
        if branchmerge:
            if pa == p2: ###she is the common ancestor
                raise util.Abort(_("can't merge with ancestor"))
            elif pa == p1: ###me is the common ancestor
                if p1.branch() != p2.branch(): ###...but we are different named branches (usually 'default')
                    fastforward = True ### Merging a branch back into common ancestor
                else:
                    raise util.Abort(_("nothing to merge (use 'hg update'"
                                       " or check 'hg heads')"))
            if not force and (wc.files() or wc.deleted()): ### edits, adds, or mods in wc
                raise util.Abort(_("outstanding uncommitted changes"))
        elif not overwrite:
            if pa == p1 or pa == p2: # linear
                pass # all good
            elif p1.branch() == p2.branch():
                if wc.files() or wc.deleted():
                    raise util.Abort(_("crosses branches (use 'hg merge' or "
                                       "'hg update -C' to discard changes)"))
                raise util.Abort(_("crosses branches (use 'hg merge' "
                                   "or 'hg update -C')"))
            elif wc.files() or wc.deleted():
                raise util.Abort(_("crosses named branches (use "
                                   "'hg update -C' to discard changes)"))
            else:
                # Allow jumping branches if there are no changes
                overwrite = True

        ### calculate phase
        action = []
        if not force:
            merge._checkunknown(wc, p2)
        #~ if not util.checkcase(repo.path):
            #~ _checkcollision(p2)
        action += merge._forgetremoved(wc, p2, branchmerge)
        action += merge.manifestmerge(repo, wc, p2, pa, overwrite, partial)

        ### apply phase
        if not branchmerge: # just jump to the new rev
            fp1, fp2, xp1, xp2 = fp2, mercurial.node.nullid, xp2, ''
        if not partial:
            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)

        stats = merge.applyupdates(repo, action, wc, p2, pa)
        
        #added: I want to record updates even if partial merge
        #merge.recordupdates(repo, action, branchmerge)
                  
        if not partial:
            repo.dirstate.setparents(fp1, fp2)
            merge.recordupdates(repo, action, branchmerge)
            if not branchmerge and not fastforward:
                repo.dirstate.setbranch(p2.branch()) 
        
    finally:
        wlock.release()
    
    if not partial:
        repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
    return stats