def validdest(repo, old, new):
    """Is the new bookmark destination a valid update from the old one"""
    repo = repo.unfiltered()
    if old == new:
        # Old == new -> nothing to update.
        return False
    elif not old:
        # old is nullrev, anything is valid.
        # (new != nullrev has been excluded by the previous check)
        return True
    elif repo.obsstore:
        return new.node() in obsolete.foreground(repo, [old.node()])
    else:
        # still an independent clause as it is lazier (and therefore faster)
        return old.descendant(new)
예제 #2
0
def validdest(repo, old, new):
    """Is the new bookmark destination a valid update from the old one"""
    repo = repo.unfiltered()
    if old == new:
        # Old == new -> nothing to update.
        return False
    elif not old:
        # old is nullrev, anything is valid.
        # (new != nullrev has been excluded by the previous check)
        return True
    elif repo.obsstore:
        return new.node() in obsolete.foreground(repo, [old.node()])
    else:
        # still an independent clause as it is lazyer (and therefore faster)
        return old.descendant(new)
예제 #3
0
def update(repo,
           node,
           branchmerge,
           force,
           partial,
           ancestor=None,
           mergeancestor=False,
           labels=None):
    """
    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)
    mergeancestor = whether it is merging with an ancestor. If true,
      we should accept the incoming changes for any prompts that occur.
      If false, merging with an ancestor (fast-forward) is only allowed
      between different named branches. This flag is used by rebase extension
      as a temporary fix and should be avoided in general.

    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.t.

    -c  -C  dirty  rev  |  linear   same  cross
     n   n    n     n   |    ok     (1)     x
     n   n    n     y   |    ok     ok     ok
     n   n    y     n   |   merge   (2)    (2)
     n   n    y     y   |   merge   (3)    (3)
     n   y    *     *   |    ---  discard  ---
     y   n    y     *   |    ---    (4)    ---
     y   n    n     *   |    ---    ok     ---
     y   y    *     *   |    ---    (5)    ---

    x = can't happen
    * = don't-care
    1 = abort: not a linear update (merge or update --check to force update)
    2 = abort: uncommitted changes (commit and merge, or update --clean to
                 discard changes)
    3 = abort: uncommitted changes (commit or update --clean to discard changes)
    4 = abort: uncommitted changes (checked in commands.py)
    5 = incompatible options (checked in commands.py)

    Return the same tuple as applyupdates().
    """

    onode = node
    wlock = repo.wlock()
    try:
        wc = repo[None]
        pl = wc.parents()
        p1 = pl[0]
        pas = [None]
        if ancestor:
            pas = [repo[ancestor]]

        if node is None:
            # Here is where we should consider bookmarks, divergent bookmarks,
            # foreground changesets (successors), and tip of current branch;
            # but currently we are only checking the branch tips.
            try:
                node = repo.branchtip(wc.branch())
            except errormod.RepoLookupError:
                if wc.branch() == "default":  # no default branch!
                    node = repo.lookup("tip")  # update to tip
                else:
                    raise util.Abort(_("branch %s not found") % wc.branch())

            if p1.obsolete() and not p1.children():
                # allow updating to successors
                successors = obsolete.successorssets(repo, p1.node())

                # behavior of certain cases is as follows,
                #
                # divergent changesets: update to highest rev, similar to what
                #     is currently done when there are more than one head
                #     (i.e. 'tip')
                #
                # replaced changesets: same as divergent except we know there
                # is no conflict
                #
                # pruned changeset: no update is done; though, we could
                #     consider updating to the first non-obsolete parent,
                #     similar to what is current done for 'hg prune'

                if successors:
                    # flatten the list here handles both divergent (len > 1)
                    # and the usual case (len = 1)
                    successors = [n for sub in successors for n in sub]

                    # get the max revision for the given successors set,
                    # i.e. the 'tip' of a set
                    node = repo.revs("max(%ln)", successors).first()
                    pas = [p1]

        overwrite = force and not branchmerge

        p2 = repo[node]
        if pas[0] is None:
            if repo.ui.config("merge", "preferancestor", '*') == '*':
                cahs = repo.changelog.commonancestorsheads(
                    p1.node(), p2.node())
                pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
            else:
                pas = [p1.ancestor(p2, warn=branchmerge)]

        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)

        ### check phase
        if not overwrite and len(pl) > 1:
            raise util.Abort(_("outstanding uncommitted merge"))
        if branchmerge:
            if pas == [p2]:
                raise util.Abort(
                    _("merging with a working directory ancestor"
                      " has no effect"))
            elif pas == [p1]:
                if not mergeancestor and p1.branch() == p2.branch():
                    raise util.Abort(_("nothing to merge"),
                                     hint=_("use 'hg update' "
                                            "or check 'hg heads'"))
            if not force and (wc.files() or wc.deleted()):
                raise util.Abort(_("uncommitted changes"),
                                 hint=_("use 'hg status' to list changes"))
            for s in sorted(wc.substate):
                if wc.sub(s).dirty():
                    raise util.Abort(
                        _("uncommitted changes in "
                          "subrepository '%s'") % s)

        elif not overwrite:
            if p1 == p2:  # no-op update
                # call the hooks and exit early
                repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
                repo.hook('update', parent1=xp2, parent2='', error=0)
                return 0, 0, 0, 0

            if pas not in ([p1], [p2]):  # nonlinear
                dirty = wc.dirty(missing=True)
                if dirty or onode is None:
                    # Branching is a bit strange to ensure we do the minimal
                    # amount of call to obsolete.background.
                    foreground = obsolete.foreground(repo, [p1.node()])
                    # note: the <node> variable contains a random identifier
                    if repo[node].node() in foreground:
                        pas = [p1]  # allow updating to successors
                    elif dirty:
                        msg = _("uncommitted changes")
                        if onode is None:
                            hint = _("commit and merge, or update --clean to"
                                     " discard changes")
                        else:
                            hint = _("commit or update --clean to discard"
                                     " changes")
                        raise util.Abort(msg, hint=hint)
                    else:  # node is none
                        msg = _("not a linear update")
                        hint = _("merge or update --check to force update")
                        raise util.Abort(msg, hint=hint)
                else:
                    # Allow jumping branches if clean and specific rev given
                    pas = [p1]

        followcopies = False
        if overwrite:
            pas = [wc]
        elif pas == [p2]:  # backwards
            pas = [wc.p1()]
        elif not branchmerge and not wc.dirty(missing=True):
            pass
        elif pas[0] and repo.ui.configbool("merge", "followcopies", True):
            followcopies = True

        ### calculate phase
        actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
                                   partial, mergeancestor, followcopies)

        ### apply phase
        if not branchmerge:  # just jump to the new rev
            fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
        if not partial:
            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
            # note that we're in the middle of an update
            repo.vfs.write('updatestate', p2.hex())

        stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)

        if not partial:
            repo.dirstate.beginparentchange()
            repo.setparents(fp1, fp2)
            recordupdates(repo, actions, branchmerge)
            # update completed, clear state
            util.unlink(repo.join('updatestate'))

            if not branchmerge:
                repo.dirstate.setbranch(p2.branch())
            repo.dirstate.endparentchange()
    finally:
        wlock.release()

    if not partial:
        repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
    return stats
예제 #4
0
def update(repo, node, branchmerge, force, partial, ancestor=None,
           mergeancestor=False):
    """
    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)
    mergeancestor = whether it is merging with an ancestor. If true,
      we should accept the incoming changes for any prompts that occur.
      If false, merging with an ancestor (fast-forward) is only allowed
      between different named branches. This flag is used by rebase extension
      as a temporary fix and should be avoided in general.

    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.t.

    -c  -C  dirty  rev  |  linear   same  cross
     n   n    n     n   |    ok     (1)     x
     n   n    n     y   |    ok     ok     ok
     n   n    y     n   |   merge   (2)    (2)
     n   n    y     y   |   merge   (3)    (3)
     n   y    *     *   |    ---  discard  ---
     y   n    y     *   |    ---    (4)    ---
     y   n    n     *   |    ---    ok     ---
     y   y    *     *   |    ---    (5)    ---

    x = can't happen
    * = don't-care
    1 = abort: not a linear update (merge or update --check to force update)
    2 = abort: uncommitted changes (commit and merge, or update --clean to
                 discard changes)
    3 = abort: uncommitted changes (commit or update --clean to discard changes)
    4 = abort: uncommitted changes (checked in commands.py)
    5 = incompatible options (checked in commands.py)

    Return the same tuple as applyupdates().
    """

    onode = node
    wlock = repo.wlock()
    try:
        wc = repo[None]
        if node is None:
            # tip of current branch
            try:
                node = repo.branchtip(wc.branch())
            except error.RepoLookupError:
                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()
        p1, p2 = pl[0], repo[node]
        if ancestor:
            pa = repo[ancestor]
        else:
            pa = p1.ancestor(p2)

        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)

        ### check phase
        if not overwrite and len(pl) > 1:
            raise util.Abort(_("outstanding uncommitted merges"))
        if branchmerge:
            if pa == p2:
                raise util.Abort(_("merging with a working directory ancestor"
                                   " has no effect"))
            elif pa == p1:
                if not mergeancestor and p1.branch() == p2.branch():
                    raise util.Abort(_("nothing to merge"),
                                     hint=_("use 'hg update' "
                                            "or check 'hg heads'"))
            if not force and (wc.files() or wc.deleted()):
                raise util.Abort(_("uncommitted changes"),
                                 hint=_("use 'hg status' to list changes"))
            for s in sorted(wc.substate):
                if wc.sub(s).dirty():
                    raise util.Abort(_("uncommitted changes in "
                                       "subrepository '%s'") % s)

        elif not overwrite:
            if pa not in (p1, p2):  # nolinear
                dirty = wc.dirty(missing=True)
                if dirty or onode is None:
                    # Branching is a bit strange to ensure we do the minimal
                    # amount of call to obsolete.background.
                    foreground = obsolete.foreground(repo, [p1.node()])
                    # note: the <node> variable contains a random identifier
                    if repo[node].node() in foreground:
                        pa = p1  # allow updating to successors
                    elif dirty:
                        msg = _("uncommitted changes")
                        if onode is None:
                            hint = _("commit and merge, or update --clean to"
                                     " discard changes")
                        else:
                            hint = _("commit or update --clean to discard"
                                     " changes")
                        raise util.Abort(msg, hint=hint)
                    else:  # node is none
                        msg = _("not a linear update")
                        hint = _("merge or update --check to force update")
                        raise util.Abort(msg, hint=hint)
                else:
                    # Allow jumping branches if clean and specific rev given
                    pa = p1

        ### calculate phase
        actions = calculateupdates(repo, wc, p2, pa,
                                   branchmerge, force, partial, mergeancestor)

        ### apply phase
        if not branchmerge: # just jump to the new rev
            fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
        if not partial:
            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
            # note that we're in the middle of an update
            repo.vfs.write('updatestate', p2.hex())

        stats = applyupdates(repo, actions, wc, p2, pa, overwrite)

        if not partial:
            repo.setparents(fp1, fp2)
            recordupdates(repo, actions, branchmerge)
            # update completed, clear state
            util.unlink(repo.join('updatestate'))

            if not branchmerge:
                repo.dirstate.setbranch(p2.branch())
    finally:
        wlock.release()

    if not partial:
        repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
    return stats
예제 #5
0
파일: merge.py 프로젝트: RayFerr000/PLTL
def update(repo, node, branchmerge, force, partial, ancestor=None,
           mergeancestor=False, labels=None):
    """
    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)
    mergeancestor = whether it is merging with an ancestor. If true,
      we should accept the incoming changes for any prompts that occur.
      If false, merging with an ancestor (fast-forward) is only allowed
      between different named branches. This flag is used by rebase extension
      as a temporary fix and should be avoided in general.

    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.t.

    -c  -C  dirty  rev  |  linear   same  cross
     n   n    n     n   |    ok     (1)     x
     n   n    n     y   |    ok     ok     ok
     n   n    y     n   |   merge   (2)    (2)
     n   n    y     y   |   merge   (3)    (3)
     n   y    *     *   |    ---  discard  ---
     y   n    y     *   |    ---    (4)    ---
     y   n    n     *   |    ---    ok     ---
     y   y    *     *   |    ---    (5)    ---

    x = can't happen
    * = don't-care
    1 = abort: not a linear update (merge or update --check to force update)
    2 = abort: uncommitted changes (commit and merge, or update --clean to
                 discard changes)
    3 = abort: uncommitted changes (commit or update --clean to discard changes)
    4 = abort: uncommitted changes (checked in commands.py)
    5 = incompatible options (checked in commands.py)

    Return the same tuple as applyupdates().
    """

    onode = node
    wlock = repo.wlock()
    try:
        wc = repo[None]
        pl = wc.parents()
        p1 = pl[0]
        pas = [None]
        if ancestor is not None:
            pas = [repo[ancestor]]

        if node is None:
            # Here is where we should consider bookmarks, divergent bookmarks,
            # foreground changesets (successors), and tip of current branch;
            # but currently we are only checking the branch tips.
            try:
                node = repo.branchtip(wc.branch())
            except errormod.RepoLookupError:
                if wc.branch() == 'default': # no default branch!
                    node = repo.lookup('tip') # update to tip
                else:
                    raise util.Abort(_("branch %s not found") % wc.branch())

            if p1.obsolete() and not p1.children():
                # allow updating to successors
                successors = obsolete.successorssets(repo, p1.node())

                # behavior of certain cases is as follows,
                #
                # divergent changesets: update to highest rev, similar to what
                #     is currently done when there are more than one head
                #     (i.e. 'tip')
                #
                # replaced changesets: same as divergent except we know there
                # is no conflict
                #
                # pruned changeset: no update is done; though, we could
                #     consider updating to the first non-obsolete parent,
                #     similar to what is current done for 'hg prune'

                if successors:
                    # flatten the list here handles both divergent (len > 1)
                    # and the usual case (len = 1)
                    successors = [n for sub in successors for n in sub]

                    # get the max revision for the given successors set,
                    # i.e. the 'tip' of a set
                    node = repo.revs('max(%ln)', successors).first()
                    pas = [p1]

        overwrite = force and not branchmerge

        p2 = repo[node]
        if pas[0] is None:
            if repo.ui.config('merge', 'preferancestor', '*') == '*':
                cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
                pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
            else:
                pas = [p1.ancestor(p2, warn=branchmerge)]

        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)

        ### check phase
        if not overwrite and len(pl) > 1:
            raise util.Abort(_("outstanding uncommitted merge"))
        if branchmerge:
            if pas == [p2]:
                raise util.Abort(_("merging with a working directory ancestor"
                                   " has no effect"))
            elif pas == [p1]:
                if not mergeancestor and p1.branch() == p2.branch():
                    raise util.Abort(_("nothing to merge"),
                                     hint=_("use 'hg update' "
                                            "or check 'hg heads'"))
            if not force and (wc.files() or wc.deleted()):
                raise util.Abort(_("uncommitted changes"),
                                 hint=_("use 'hg status' to list changes"))
            for s in sorted(wc.substate):
                wc.sub(s).bailifchanged()

        elif not overwrite:
            if p1 == p2: # no-op update
                # call the hooks and exit early
                repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
                repo.hook('update', parent1=xp2, parent2='', error=0)
                return 0, 0, 0, 0

            if pas not in ([p1], [p2]):  # nonlinear
                dirty = wc.dirty(missing=True)
                if dirty or onode is None:
                    # Branching is a bit strange to ensure we do the minimal
                    # amount of call to obsolete.background.
                    foreground = obsolete.foreground(repo, [p1.node()])
                    # note: the <node> variable contains a random identifier
                    if repo[node].node() in foreground:
                        pas = [p1]  # allow updating to successors
                    elif dirty:
                        msg = _("uncommitted changes")
                        if onode is None:
                            hint = _("commit and merge, or update --clean to"
                                     " discard changes")
                        else:
                            hint = _("commit or update --clean to discard"
                                     " changes")
                        raise util.Abort(msg, hint=hint)
                    else:  # node is none
                        msg = _("not a linear update")
                        hint = _("merge or update --check to force update")
                        raise util.Abort(msg, hint=hint)
                else:
                    # Allow jumping branches if clean and specific rev given
                    pas = [p1]

        followcopies = False
        if overwrite:
            pas = [wc]
        elif pas == [p2]: # backwards
            pas = [wc.p1()]
        elif not branchmerge and not wc.dirty(missing=True):
            pass
        elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
            followcopies = True

        ### calculate phase
        actionbyfile, diverge, renamedelete = calculateupdates(
            repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
            followcopies)
        # Convert to dictionary-of-lists format
        actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
        for f, (m, args, msg) in actionbyfile.iteritems():
            if m not in actions:
                actions[m] = []
            actions[m].append((f, args, msg))

        if not util.checkcase(repo.path):
            # check collision between files only in p2 for clean update
            if (not branchmerge and
                (force or not wc.dirty(missing=True, branch=False))):
                _checkcollision(repo, p2.manifest(), None)
            else:
                _checkcollision(repo, wc.manifest(), actions)

        # Prompt and create actions. TODO: Move this towards resolve phase.
        for f, args, msg in sorted(actions['cd']):
            if repo.ui.promptchoice(
                _("local changed %s which remote deleted\n"
                  "use (c)hanged version or (d)elete?"
                  "$$ &Changed $$ &Delete") % f, 0):
                actions['r'].append((f, None, "prompt delete"))
            else:
                actions['a'].append((f, None, "prompt keep"))
        del actions['cd'][:]

        for f, args, msg in sorted(actions['dc']):
            flags, = args
            if repo.ui.promptchoice(
                _("remote changed %s which local deleted\n"
                  "use (c)hanged version or leave (d)eleted?"
                  "$$ &Changed $$ &Deleted") % f, 0) == 0:
                actions['g'].append((f, (flags,), "prompt recreating"))
        del actions['dc'][:]

        ### apply phase
        if not branchmerge: # just jump to the new rev
            fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
        if not partial:
            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
            # note that we're in the middle of an update
            repo.vfs.write('updatestate', p2.hex())

        stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)

        # divergent renames
        for f, fl in sorted(diverge.iteritems()):
            repo.ui.warn(_("note: possible conflict - %s was renamed "
                           "multiple times to:\n") % f)
            for nf in fl:
                repo.ui.warn(" %s\n" % nf)

        # rename and delete
        for f, fl in sorted(renamedelete.iteritems()):
            repo.ui.warn(_("note: possible conflict - %s was deleted "
                           "and renamed to:\n") % f)
            for nf in fl:
                repo.ui.warn(" %s\n" % nf)

        if not partial:
            repo.dirstate.beginparentchange()
            repo.setparents(fp1, fp2)
            recordupdates(repo, actions, branchmerge)
            # update completed, clear state
            util.unlink(repo.join('updatestate'))

            if not branchmerge:
                repo.dirstate.setbranch(p2.branch())
            repo.dirstate.endparentchange()
    finally:
        wlock.release()

    if not partial:
        def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
            repo.hook('update', parent1=parent1, parent2=parent2, error=error)
        repo._afterlock(updatehook)
    return stats
예제 #6
0
def update(repo,
           node,
           branchmerge,
           force,
           partial,
           ancestor=None,
           mergeancestor=False):
    """
    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)
    mergeancestor = whether it is merging with an ancestor. If true,
      we should accept the incoming changes for any prompts that occur.
      If false, merging with an ancestor (fast-forward) is only allowed
      between different named branches. This flag is used by rebase extension
      as a temporary fix and should be avoided in general.

    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.t.

    -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)

    Return the same tuple as applyupdates().
    """

    onode = node
    wlock = repo.wlock()
    try:
        wc = repo[None]
        if node is None:
            # tip of current branch
            try:
                node = repo.branchtip(wc.branch())
            except error.RepoLookupError:
                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()
        p1, p2 = pl[0], repo[node]
        if ancestor:
            pa = repo[ancestor]
        else:
            pa = p1.ancestor(p2)

        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)

        ### check phase
        if not overwrite and len(pl) > 1:
            raise util.Abort(_("outstanding uncommitted merges"))
        if branchmerge:
            if pa == p2:
                raise util.Abort(
                    _("merging with a working directory ancestor"
                      " has no effect"))
            elif pa == p1:
                if not mergeancestor and p1.branch() == p2.branch():
                    raise util.Abort(_("nothing to merge"),
                                     hint=_("use 'hg update' "
                                            "or check 'hg heads'"))
            if not force and (wc.files() or wc.deleted()):
                raise util.Abort(_("outstanding uncommitted changes"),
                                 hint=_("use 'hg status' to list changes"))
            for s in sorted(wc.substate):
                if wc.sub(s).dirty():
                    raise util.Abort(
                        _("outstanding uncommitted changes in "
                          "subrepository '%s'") % s)

        elif not overwrite:
            if pa not in (p1, p2):  # nolinear
                dirty = wc.dirty(missing=True)
                if dirty or onode is None:
                    # Branching is a bit strange to ensure we do the minimal
                    # amount of call to obsolete.background.
                    foreground = obsolete.foreground(repo, [p1.node()])
                    # note: the <node> variable contains a random identifier
                    if repo[node].node() in foreground:
                        pa = p1  # allow updating to successors
                    elif dirty:
                        msg = _("crosses branches (merge branches or use"
                                " --clean to discard changes)")
                        raise util.Abort(msg)
                    else:  # node is none
                        msg = _("crosses branches (merge branches or update"
                                " --check to force update)")
                        raise util.Abort(msg)
                else:
                    # Allow jumping branches if clean and specific rev given
                    pa = p1

        ### calculate phase
        actions = calculateupdates(repo, wc, p2, pa, branchmerge, force,
                                   partial, mergeancestor)

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

        stats = applyupdates(repo, actions, wc, p2, pa, overwrite)

        if not partial:
            repo.setparents(fp1, fp2)
            recordupdates(repo, actions, branchmerge)
            if not branchmerge:
                repo.dirstate.setbranch(p2.branch())
    finally:
        wlock.release()

    if not partial:
        repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
    return stats
예제 #7
0
def update(repo, node, branchmerge, force, partial, ancestor=None,
           mergeancestor=False):
    """
    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)
    mergeancestor = whether it is merging with an ancestor. If true,
      we should accept the incoming changes for any prompts that occur.
      If false, merging with an ancestor (fast-forward) is only allowed
      between different named branches. This flag is used by rebase extension
      as a temporary fix and should be avoided in general.

    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.t.

    -c  -C  dirty  rev  |  linear   same  cross
     n   n    n     n   |    ok     (1)     x
     n   n    n     y   |    ok     ok     ok
     n   n    y     n   |   merge   (2)    (2)
     n   n    y     y   |   merge   (3)    (3)
     n   y    *     *   |    ---  discard  ---
     y   n    y     *   |    ---    (4)    ---
     y   n    n     *   |    ---    ok     ---
     y   y    *     *   |    ---    (5)    ---

    x = can't happen
    * = don't-care
    1 = abort: not a linear update (merge or update --check to force update)
    2 = abort: uncommitted changes (commit and merge, or update --clean to
                 discard changes)
    3 = abort: uncommitted changes (commit or update --clean to discard changes)
    4 = abort: uncommitted changes (checked in commands.py)
    5 = incompatible options (checked in commands.py)

    Return the same tuple as applyupdates().
    """

    onode = node
    wlock = repo.wlock()
    try:
        wc = repo[None]
        pl = wc.parents()
        p1 = pl[0]
        pas = [None]
        if ancestor:
            pas = [repo[ancestor]]

        if node is None:
            # Here is where we should consider bookmarks, divergent bookmarks,
            # foreground changesets (successors), and tip of current branch;
            # but currently we are only checking the branch tips.
            try:
                node = repo.branchtip(wc.branch())
            except error.RepoLookupError:
                if wc.branch() == "default": # no default branch!
                    node = repo.lookup("tip") # update to tip
                else:
                    raise util.Abort(_("branch %s not found") % wc.branch())

            if p1.obsolete() and not p1.children():
                # allow updating to successors
                successors = obsolete.successorssets(repo, p1.node())

                # behavior of certain cases is as follows,
                #
                # divergent changesets: update to highest rev, similar to what
                #     is currently done when there are more than one head
                #     (i.e. 'tip')
                #
                # replaced changesets: same as divergent except we know there
                # is no conflict
                #
                # pruned changeset: no update is done; though, we could
                #     consider updating to the first non-obsolete parent,
                #     similar to what is current done for 'hg prune'

                if successors:
                    # flatten the list here handles both divergent (len > 1)
                    # and the usual case (len = 1)
                    successors = [n for sub in successors for n in sub]

                    # get the max revision for the given successors set,
                    # i.e. the 'tip' of a set
                    node = repo.revs("max(%ln)", successors)[0]
                    pas = [p1]

        overwrite = force and not branchmerge

        p2 = repo[node]
        if pas[0] is None:
            if repo.ui.config("merge", "preferancestor") == '*':
                cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
                pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
            else:
                pas = [p1.ancestor(p2, warn=True)]

        fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)

        ### check phase
        if not overwrite and len(pl) > 1:
            raise util.Abort(_("outstanding uncommitted merges"))
        if branchmerge:
            if pas == [p2]:
                raise util.Abort(_("merging with a working directory ancestor"
                                   " has no effect"))
            elif pas == [p1]:
                if not mergeancestor and p1.branch() == p2.branch():
                    raise util.Abort(_("nothing to merge"),
                                     hint=_("use 'hg update' "
                                            "or check 'hg heads'"))
            if not force and (wc.files() or wc.deleted()):
                raise util.Abort(_("uncommitted changes"),
                                 hint=_("use 'hg status' to list changes"))
            for s in sorted(wc.substate):
                if wc.sub(s).dirty():
                    raise util.Abort(_("uncommitted changes in "
                                       "subrepository '%s'") % s)

        elif not overwrite:
            if p1 == p2: # no-op update
                # call the hooks and exit early
                repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
                repo.hook('update', parent1=xp2, parent2='', error=0)
                return 0, 0, 0, 0

            if pas not in ([p1], [p2]):  # nonlinear
                dirty = wc.dirty(missing=True)
                if dirty or onode is None:
                    # Branching is a bit strange to ensure we do the minimal
                    # amount of call to obsolete.background.
                    foreground = obsolete.foreground(repo, [p1.node()])
                    # note: the <node> variable contains a random identifier
                    if repo[node].node() in foreground:
                        pas = [p1]  # allow updating to successors
                    elif dirty:
                        msg = _("uncommitted changes")
                        if onode is None:
                            hint = _("commit and merge, or update --clean to"
                                     " discard changes")
                        else:
                            hint = _("commit or update --clean to discard"
                                     " changes")
                        raise util.Abort(msg, hint=hint)
                    else:  # node is none
                        msg = _("not a linear update")
                        hint = _("merge or update --check to force update")
                        raise util.Abort(msg, hint=hint)
                else:
                    # Allow jumping branches if clean and specific rev given
                    pas = [p1]

        followcopies = False
        if overwrite:
            pas = [wc]
        elif pas == [p2]: # backwards
            pas = [wc.p1()]
        elif not branchmerge and not wc.dirty(missing=True):
            pass
        elif pas[0] and repo.ui.configbool("merge", "followcopies", True):
            followcopies = True

        ### calculate phase
        actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
                                   partial, mergeancestor, followcopies)

        ### apply phase
        if not branchmerge: # just jump to the new rev
            fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
        if not partial:
            repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
            # note that we're in the middle of an update
            repo.vfs.write('updatestate', p2.hex())

        stats = applyupdates(repo, actions, wc, p2, overwrite)

        if not partial:
            repo.setparents(fp1, fp2)
            recordupdates(repo, actions, branchmerge)
            # update completed, clear state
            util.unlink(repo.join('updatestate'))

            if not branchmerge:
                repo.dirstate.setbranch(p2.branch())
    finally:
        wlock.release()

    if not partial:
        repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
    return stats