Exemple #1
0
        def prunetemporaryincludes(self):
            if repo.vfs.exists('tempsparse'):
                origstatus = self.status()
                modified, added, removed, deleted, a, b, c = origstatus
                if modified or added or removed or deleted:
                    # Still have pending changes. Don't bother trying to prune.
                    return

                sparsematch = self.sparsematch(includetemp=False)
                dirstate = self.dirstate
                actions = []
                dropped = []
                tempincludes = self.gettemporaryincludes()
                for file in tempincludes:
                    if file in dirstate and not sparsematch(file):
                        message = 'dropping temporarily included sparse files'
                        actions.append((file, None, message))
                        dropped.append(file)

                typeactions = collections.defaultdict(list)
                typeactions['r'] = actions
                mergemod.applyupdates(self, typeactions, self[None], self['.'],
                                      False)

                # Fix dirstate
                for file in dropped:
                    dirstate.drop(file)

                self.vfs.unlink('tempsparse')
                self.invalidatesignaturecache()
                msg = _("cleaned up %d temporarily added file(s) from the "
                        "sparse checkout\n")
                ui.status(msg % len(tempincludes))
Exemple #2
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)
Exemple #3
0
def _applyupdates(repo, actions, wctx, dest, labels, conflicts):
    numerrors = sum(1 for c in conflicts if c.type == ConflictType.ERROR)

    # Call applyupdates
    # Note that applyupdates may mutate actions.
    stats = mergemod.applyupdates(repo,
                                  actions,
                                  wctx,
                                  dest,
                                  overwrite=False,
                                  labels=labels)

    # Add the error count to the number of unresolved files.
    # This ensures we exit unsuccessfully if there were any errors
    return (stats[0], stats[1], stats[2], stats[3] + numerrors), actions
Exemple #4
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)
Exemple #5
0
def _refresh(ui, repo, origstatus, origsparsematch, force):
    """Refreshes which files are on disk by comparing the old status and
    sparsematch with the new sparsematch.

    Will raise an exception if a file with pending changes is being excluded
    or included (unless force=True).
    """
    modified, added, removed, deleted, unknown, ignored, clean = origstatus

    # Verify there are no pending changes
    pending = set()
    pending.update(modified)
    pending.update(added)
    pending.update(removed)
    sparsematch = repo.sparsematch()
    abort = False
    for file in pending:
        if not sparsematch(file):
            ui.warn(_("pending changes to '%s'\n") % file)
            abort = not force
    if abort:
        raise error.Abort(_("could not update sparseness due to " +
            "pending changes"))

    # Calculate actions
    dirstate = repo.dirstate
    ctx = repo['.']
    added = []
    lookup = []
    dropped = []
    mf = ctx.manifest()
    files = set(mf)

    actions = {}

    for file in files:
        old = origsparsematch(file)
        new = sparsematch(file)
        # Add files that are newly included, or that don't exist in
        # the dirstate yet.
        if (new and not old) or (old and new and not file in dirstate):
            fl = mf.flags(file)
            if repo.wvfs.exists(file):
                actions[file] = ('e', (fl,), '')
                lookup.append(file)
            else:
                actions[file] = ('g', (fl, False), '')
                added.append(file)
        # Drop files that are newly excluded, or that still exist in
        # the dirstate.
        elif (old and not new) or (not old and not new and file in dirstate):
            dropped.append(file)
            if file not in pending:
                actions[file] = ('r', [], '')

    # Verify there are no pending changes in newly included files
    abort = False
    for file in lookup:
        ui.warn(_("pending changes to '%s'\n") % file)
        abort = not force
    if abort:
        raise error.Abort(_("cannot change sparseness due to " +
            "pending changes (delete the files or use --force " +
            "to bring them back dirty)"))

    # Check for files that were only in the dirstate.
    for file, state in dirstate.iteritems():
        if not file in files:
            old = origsparsematch(file)
            new = sparsematch(file)
            if old and not new:
                dropped.append(file)

    # Apply changes to disk
    typeactions = dict((m, [])
                       for m in 'a f g am cd dc r dm dg m e k p pr'.split())
    for f, (m, args, msg) in actions.iteritems():
        if m not in typeactions:
            typeactions[m] = []
        typeactions[m].append((f, args, msg))
    mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False)

    # Fix dirstate
    for file in added:
        dirstate.normal(file)

    for file in dropped:
        dirstate.drop(file)

    for file in lookup:
        # File exists on disk, and we're bringing it back in an unknown state.
        dirstate.normallookup(file)

    return added, dropped, lookup
Exemple #6
0
    def _calculateupdates(orig, repo, wctx, mctx, ancestors, branchmerge, *arg,
                          **kwargs):
        """Filter updates to only lay out files that match the sparse rules.
        """
        actions, diverge, renamedelete = orig(repo, wctx, mctx, ancestors,
                                              branchmerge, *arg, **kwargs)

        if not util.safehasattr(repo, 'sparsematch'):
            return actions, diverge, renamedelete

        files = set()
        prunedactions = {}
        oldrevs = [pctx.rev() for pctx in wctx.parents()]
        oldsparsematch = repo.sparsematch(*oldrevs)

        if branchmerge:
            # If we're merging, use the wctx filter, since we're merging into
            # the wctx.
            sparsematch = repo.sparsematch(wctx.parents()[0].rev())
        else:
            # If we're updating, use the target context's filter, since we're
            # moving to the target context.
            sparsematch = repo.sparsematch(mctx.rev())

        temporaryfiles = []
        for file, action in actions.iteritems():
            type, args, msg = action
            files.add(file)
            if sparsematch(file):
                prunedactions[file] = action
            elif type == 'm':
                temporaryfiles.append(file)
                prunedactions[file] = action
            elif branchmerge:
                if type != 'k':
                    temporaryfiles.append(file)
                    prunedactions[file] = action
            elif type == 'f':
                prunedactions[file] = action
            elif file in wctx:
                prunedactions[file] = ('r', args, msg)

        if len(temporaryfiles) > 0:
            ui.status(_("temporarily included %d file(s) in the sparse checkout"
                " for merging\n") % len(temporaryfiles))
            repo.addtemporaryincludes(temporaryfiles)

            # Add the new files to the working copy so they can be merged, etc
            actions = []
            message = 'temporarily adding to sparse checkout'
            wctxmanifest = repo[None].manifest()
            for file in temporaryfiles:
                if file in wctxmanifest:
                    fctx = repo[None][file]
                    actions.append((file, (fctx.flags(), False), message))

            typeactions = collections.defaultdict(list)
            typeactions['g'] = actions
            mergemod.applyupdates(repo, typeactions, repo[None], repo['.'],
                                  False)

            dirstate = repo.dirstate
            for file, flags, msg in actions:
                dirstate.normal(file)

        profiles = repo.getactiveprofiles()
        changedprofiles = profiles & files
        # If an active profile changed during the update, refresh the checkout.
        # Don't do this during a branch merge, since all incoming changes should
        # have been handled by the temporary includes above.
        if changedprofiles and not branchmerge:
            mf = mctx.manifest()
            for file in mf:
                old = oldsparsematch(file)
                new = sparsematch(file)
                if not old and new:
                    flags = mf.flags(file)
                    prunedactions[file] = ('g', (flags, False), '')
                elif old and not new:
                    prunedactions[file] = ('r', [], '')

        return prunedactions, diverge, renamedelete
Exemple #7
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
Exemple #8
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
Exemple #9
0
def _handleupdateconflicts(repo, wctx, src, dest, labels, conflicts):
    # When resolving conflicts during an update operation, the working
    # directory (wctx) is one side of the merge, the destination commit (dest)
    # is the other side of the merge, and the source commit (src) is treated as
    # the common ancestor.
    #
    # This is what we want with respect to the graph topology.  If we are
    # updating from commit A (src) to B (dest), and the real ancestor is C, we
    # effectively treat the update operation as reverting all commits from A to
    # C, then applying the commits from C to B.  We are then trying to re-apply
    # the local changes in the working directory (against A) to the new
    # location B.  Using A as the common ancestor in this operation is the
    # desired behavior.

    # Build a list of actions to pass to mergemod.applyupdates()
    actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
    numerrors = 0
    for conflict in conflicts:
        # The action tuple is:
        # - path_in_1, path_in_2, path_in_ancestor, move, ancestor_node

        if conflict.type == ConflictType.ERROR:
            # We don't record this as a conflict for now.
            # We will report the error, but the file will show modified in
            # the working directory status after the update returns.
            repo.ui.write_err(
                _('error updating %s: %s\n') %
                (conflict.path, conflict.message))
            numerrors += 1
            continue
        elif conflict.type == ConflictType.MODIFIED_REMOVED:
            action_type = 'cd'
            action = (conflict.path, None, conflict.path, False, src.node())
            prompt = "prompt changed/deleted"
        elif conflict.type == ConflictType.UNTRACKED_ADDED:
            action_type = 'c'
            action = (dest.manifest().flags(conflict.path), )
            prompt = "remote created"
        elif conflict.type == ConflictType.REMOVED_MODIFIED:
            action_type = 'dc'
            action = (None, conflict.path, conflict.path, False, src.node())
            prompt = "prompt deleted/changed"
        elif conflict.type == ConflictType.MISSING_REMOVED:
            # Nothing to do here really.  The file was already removed
            # locally in the working directory before, and it was removed
            # in the new commit.
            continue
        elif conflict.type == ConflictType.MODIFIED:
            action_type = 'm'
            action = (conflict.path, conflict.path, conflict.path, False,
                      src.node())
            prompt = "versions differ"
        else:
            raise Exception('unknown conflict type received from eden: '
                            '%r, %r, %r' %
                            (conflict.type, conflict.path, conflict.message))

        actions[action_type].append((conflict.path, action, prompt))

    # Call applyupdates
    stats = mergemod.applyupdates(repo,
                                  actions,
                                  wctx,
                                  dest,
                                  overwrite=False,
                                  labels=labels)

    # Add the error count to the number of unresolved files.
    # This ensures we exit unsuccessfully if there were any errors
    return (stats[0], stats[1], stats[2], stats[3] + numerrors)