WorkSpace.py revision c959a081a8aebb76386b6d8ea3afa850e328f6c7
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# This program is free software; you can redistribute it and/or modify
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# it under the terms of the GNU General Public License version 2
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# as published by the Free Software Foundation.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# This program is distributed in the hope that it will be useful,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# but WITHOUT ANY WARRANTY; without even the implied warranty of
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# GNU General Public License for more details.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# You should have received a copy of the GNU General Public License
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# along with this program; if not, write to the Free Software
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Use is subject to license terms.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Theory:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Workspaces have a non-binding parent/child relationship.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# All important operations apply to the changes between the two.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# However, for the sake of remote operation, the 'parent' of a
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# workspace is not seen as a literal entity, instead the figurative
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# parent contains the last changeset common to both parent and child,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# as such the 'parent tip' is actually nothing of the sort, but instead a
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# convenient imitation.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Any change made to a workspace is a change to a file therein, such
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# changes can be represented briefly as whether the file was
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# modified/added/removed as compared to the parent workspace, whether
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# the file has a different name in the parent and if so, whether it
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# was renamed or merely copied. Each changed file has an
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# associated ActiveEntry.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# The ActiveList being a list ActiveEntrys can thus present the entire
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# change in workspace state between a parent and its child, and is the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# important bit here (in that if it is incorrect, everything else will
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# be as incorrect, or more)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonimport cStringIO
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonimport os
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowefrom mercurial import cmdutil, context, hg, node, patch, repair, util
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom hgext import mq
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowefrom onbld.Scm import Version
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe#
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe# Mercurial >= 1.2 has its exception types in a mercurial.error
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe# module, prior versions had them in their associated modules.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe#
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Loweif Version.at_least("1.2"):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe from mercurial import error
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe HgRepoError = error.RepoError
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe HgLookupError = error.LookupError
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Loweelse:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe from mercurial import repo, revlog
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe HgRepoError = repo.RepoError
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe HgLookupError = revlog.LookupError
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass ActiveEntry(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Representation of the changes made to a single file.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson MODIFIED - Contents changed, but no other changes were made
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ADDED - File is newly created
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson REMOVED - File is being removed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Copies are represented by an Entry whose .parentname is non-nil
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Truly copied files have non-nil .parentname and .renamed = False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Renames have non-nil .parentname and .renamed = True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Do not access any of this information directly, do so via the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson .is_<change>() methods.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson MODIFIED = 1
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ADDED = 2
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson REMOVED = 3
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, name):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.name = name
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.change = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.parentname = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # As opposed to copied (or neither)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.renamed = False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.comments = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # ActiveEntrys sort by the name of the file they represent.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __cmp__(self, other):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return cmp(self.name, other.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_added(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.change == self.ADDED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_modified(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.change == self.MODIFIED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_removed(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.change == self.REMOVED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_renamed(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.parentname and self.renamed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_copied(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.parentname and not self.renamed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass ActiveList(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Complete representation of workspace change.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson In practice, a container for ActiveEntrys, and methods to build them,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson update them, and deal with them en masse.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, ws, parenttip, revs=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._active = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws = ws
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.revs = revs
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.base = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.parenttip = parenttip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If we couldn't find a parenttip, the two repositories must
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # be unrelated (Hg catches most of this, but this case is valid for it
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # but invalid for us)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self.parenttip == None:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('repository is unrelated')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.localtip = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if revs:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.base = revs[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.localtip = revs[-1]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._comments = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._build(revs)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _build(self, revs):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not revs:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson status = self.ws.status(self.parenttip.node(), self.localtip.node())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson files = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for ctype in status.values():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson files.extend(ctype)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # When a file is renamed, two operations actually occur.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # A file copy from source to dest and a removal of source.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # These are represented as two distinct entries in the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # changectx and status (one on the dest file for the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # copy, one on the source file for the remove).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Since these are unconnected in both the context and
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # status we can only make the association by explicitly
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # looking for it.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We deal with this thusly:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We maintain a dict dest -> source of all copies
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (updating dest as appropriate, but leaving source alone).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # After all other processing, we mark as renamed any pair
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # where source is on the removed list.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson copies = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Walk revs looking for renames and adding files that
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # are in both change context and status to the active
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # list.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for ctx in revs:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson desc = ctx.description().splitlines()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._comments.extend(desc)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for fname in ctx.files():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We store comments per-entry as well, for the sake of
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # webrev and similar. We store twice to avoid the problems
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # of uniquifying comments for the general list (and possibly
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # destroying multi-line entities in the process).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fname not in self:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._addentry(fname)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self[fname].comments.extend(desc)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fctx = ctx.filectx(fname)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe except HgLookupError:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # NB: .renamed() is a misnomer, this actually checks
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # for copies.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rn = fctx.renamed()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rn:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If the source file is a known copy we know its
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # ancestry leads us to the parent.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Otherwise make sure the source file is known to
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # be in the parent, we need not care otherwise.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We detect cycles at a later point. There is no
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # reason to continuously handle them.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rn[0] in copies:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson copies[fname] = copies[rn[0]]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elif rn[0] in self.parenttip.manifest():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson copies[fname] = rn[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Walk the copy list marking as copied any non-cyclic pair
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # where the destination file is still present in the local
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # tip (to avoid ephemeral changes)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Where source is removed, mark as renamed, and remove the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # AL entry for the source file
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for fname, oldname in copies.iteritems():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fname == oldname or fname not in self.localtip.manifest():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self[fname].parentname = oldname
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if oldname in status['removed']:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self[fname].renamed = True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if oldname in self:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson del self[oldname]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Walk the active list setting the change type for each active
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # file.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # In the case of modified files that are not renames or
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # copies, we do a content comparison, and drop entries that
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # are not actually modified.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We walk a copy of the AL such that we can drop entries
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # within the loop.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for entry in self._active.values():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if entry.name not in files:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson del self[entry.name]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if entry.name in status['added']:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson entry.change = ActiveEntry.ADDED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elif entry.name in status['removed']:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson entry.change = ActiveEntry.REMOVED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elif entry.name in status['modified']:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson entry.change = ActiveEntry.MODIFIED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # There are cases during a merge where a file will be in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # the status return as modified, but in reality be an
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # addition (ie, not in the parenttip).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We need to check whether the file is actually present
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # in the parenttip, and set it as an add, if not.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if entry.name not in self.parenttip.manifest():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson entry.change = ActiveEntry.ADDED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elif entry.is_modified() and not entry.parentname:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not self.filecmp(entry):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson del self[entry.name]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson assert entry.change
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __contains__(self, fname):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return fname in self._active
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __getitem__(self, key):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self._active[key]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __setitem__(self, key, value):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._active[key] = value
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __delitem__(self, key):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson del self._active[key]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __iter__(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for entry in self._active.values():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson yield entry
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _addentry(self, fname):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fname not in self:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self[fname] = ActiveEntry(fname)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def files(self):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Return the list of pathnames of all files touched by this
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ActiveList
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe Where files have been renamed, this will include both their
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe current name and the name which they had in the parent tip.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ret = self._active.keys()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret.extend([x.parentname for x in self
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if x.is_renamed() and x.parentname not in ret])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return ret
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def comments(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self._comments
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # It's not uncommon for a child workspace to itself contain the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # merge of several other children, with initial branch points in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # the parent (possibly from the cset a project gate was created
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # from, for instance).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Immediately after recommit, this leaves us looking like this:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # * <- recommitted changeset (real tip)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # |
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | * <- Local tip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # |/|
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # * | <- parent tip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | |\
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | | |
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | | |\
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | | | |
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | * | | <- Base
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # |/_/__/
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # [left-most is parent, next is child, right two being
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # branches in child, intermediate merges parent->child
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # omitted]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Obviously stripping base (the first child-specific delta on the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # main child workspace line) doesn't remove the vestigial branches
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # from other workspaces (or in-workspace branches, or whatever)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # In reality, what we need to strip in a recommit is any
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # child-specific branch descended from the parent (rather than
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # another part of the child). Note that this by its very nature
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # includes the branch representing the 'main' child workspace.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We calculate these by walking from base (which is guaranteed to
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # be the oldest child-local cset) to localtip searching for
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # changesets with only one parent cset, and where that cset is not
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # part of the active list (and is therefore outgoing).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def bases(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Find the bases that in combination define the "old"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson side of a recommitted set of changes, based on AL'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson get = util.cachefunc(lambda r: self.ws.repo.changectx(r).changeset())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We don't rebuild the AL So the AL local tip is the old tip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson revrange = "%s:%s" % (self.base.rev(), self.localtip.rev())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson changeiter = cmdutil.walkchangerevs(self.ws.repo.ui, self.ws.repo,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson [], get, {'rev': [revrange]})[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson hold = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson alrevs = [x.rev() for x in self.revs]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for st, rev, fns in changeiter:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson n = self.ws.repo.changelog.node(rev)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if st == 'add':
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rev in alrevs:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson hold.append(n)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elif st == 'iter':
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if n not in hold:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson p = self.ws.repo.changelog.parents(n)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if p[1] != node.nullid:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self.ws.repo.changectx(p[0]).rev() not in alrevs:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret.append(n)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return ret
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def tags(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Find tags that refer to a changeset in the ActiveList,
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe returning a list of 3-tuples (tag, node, is_local) for each.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe We return all instances of a tag that refer to such a node,
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe not just that which takes precedence.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def colliding_tags(iterable, nodes, local):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe for nd, name in [line.rstrip().split(' ', 1) for line in iterable]:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if nd in nodes:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe yield (name, self.ws.repo.lookup(nd), local)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags = []
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe nodes = set(node.hex(ctx.node()) for ctx in self.revs)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if os.path.exists(self.ws.repo.join('localtags')):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh = self.ws.repo.opener('localtags')
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe tags.extend(colliding_tags(fh, nodes, True))
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # We want to use the tags file from the localtip
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if '.hgtags' in self.localtip:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe data = self.localtip.filectx('.hgtags').data().splitlines()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe tags.extend(colliding_tags(data, nodes, False))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return tags
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def prune_tags(self, data):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Return a copy of data, which should correspond to the
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe contents of a Mercurial tags file, with any tags that refer to
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe changesets which are components of the ActiveList removed.'''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe nodes = set(node.hex(ctx.node()) for ctx in self.revs)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return [t for t in data if t.split(' ', 1)[0] not in nodes]
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def filecmp(self, entry):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Compare two revisions of two files
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Return True if file changed, False otherwise.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson The fast path compares file metadata, slow path is a
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson real comparison of file content.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parentfile = self.parenttip.filectx(entry.parentname or entry.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson localfile = self.localtip.filectx(entry.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # NB: Keep these ordered such as to make every attempt
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # to short-circuit the more time consuming checks.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parentfile.size() != localfile.size():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if parentfile.flags() != localfile.flags():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parentfile.cmp(localfile.data()):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def context(self, message, user):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Return a Mercurial context object representing the entire
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ActiveList as one change.'''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return activectx(self, message, user)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Loweclass activectx(context.memctx):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Represent an ActiveList as a Mercurial context object.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe Part of the WorkSpace.squishdeltas implementation.'''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def __init__(self, active, message, user):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Build an activectx object.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe active - The ActiveList object used as the source for all data.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe message - Changeset description
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe user - Committing user'''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def filectxfn(repository, ctx, fname):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fctx = active.localtip.filectx(fname)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe data = fctx.data()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # .hgtags is a special case, tags referring to active list
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # component changesets should be elided.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if fname == '.hgtags':
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe data = '\n'.join(active.prune_tags(data.splitlines()))
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return context.memfilectx(fname, data, 'l' in fctx.flags(),
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe 'x' in fctx.flags(),
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe active[fname].parentname)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.__active = active
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe parents = (active.parenttip.node(), node.nullid)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe extra = {'branch': active.localtip.branch()}
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe context.memctx.__init__(self, active.ws.repo, parents, message,
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe active.files(), filectxfn, user=user,
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe extra=extra)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def modified(self):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return [entry.name for entry in self.__active if entry.is_modified()]
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def added(self):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return [entry.name for entry in self.__active if entry.is_added()]
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def removed(self):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ret = [entry.name for entry in self.__active if entry.is_removed()]
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ret.extend([x.parentname for x in self.__active if x.is_renamed()])
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return ret
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def files(self):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return self.__active.files()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass WorkSpace(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, repository):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo = repository
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui = self.repo.ui
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.name = self.repo.root
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.activecache = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def parent(self, spec=None):
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe '''Return the canonical workspace parent, either SPEC (which
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe will be expanded) if provided or the default parent
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe otherwise.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if spec:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe return self.ui.expandpath(spec)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe p = self.ui.expandpath('default')
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if p == 'default':
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe return None
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe else:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe return p
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe def _localtip(self, outgoing, wctx):
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe '''Return the most representative changeset to act as the
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe localtip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe If the working directory is modified (has file changes, is a
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe merge, or has switched branches), this will be a workingctx.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe If the working directory is unmodified, this will be the most
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe recent (highest revision number) local (outgoing) head on the
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe current branch, if no heads are determined to be outgoing, it
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe will be the most recent head on the current branch.
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe '''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe # A modified working copy is seen as a proto-branch, and thus
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe # our only option as the local tip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (wctx.files() or len(wctx.parents()) > 1 or
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx.branch() != wctx.parents()[0].branch()):
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe return wctx
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe heads = self.repo.heads(start=wctx.parents()[0].node())
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe headctxs = [self.repo.changectx(n) for n in heads]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe localctxs = [c for c in headctxs if c.node() in outgoing]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ltip = sorted(localctxs or headctxs, key=lambda x: x.rev())[-1]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if len(heads) > 1:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe self.ui.warn('The current branch has more than one head, '
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe 'using %s\n' % ltip.rev())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe return ltip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe def _parenttip(self, heads, outgoing):
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe '''Return the highest-numbered, non-outgoing changeset that is
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe an ancestor of a changeset in heads.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe This is intended to find the most recent changeset on a given
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe branch that is shared between a parent and child workspace,
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe such that it can act as a stand-in for the parent workspace.
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe '''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def tipmost_shared(head, outnodes):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return the tipmost node on the same branch as head that is not
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson in outnodes.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson We walk from head to the bottom of the workspace (revision
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 0) collecting nodes not in outnodes during the add phase
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson and return the first node we see in the iter phase that
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson was previously collected.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe If no node is found (all revisions >= 0 are outgoing), the
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe only possible parenttip is the null node (node.nullid)
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe which is returned explicitly.
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson See the docstring of mercurial.cmdutil.walkchangerevs()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for the phased approach to the iterator returned. The
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson important part to note is that the 'add' phase gathers
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson nodes, which the 'iter' phase then iterates through.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe opts = {'rev': ['%s:0' % head.rev()],
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe 'follow': True}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson get = util.cachefunc(lambda r: self.repo.changectx(r).changeset())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson changeiter = cmdutil.walkchangerevs(self.repo.ui, self.repo, [],
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe get, opts)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson seen = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for st, rev, fns in changeiter:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson n = self.repo.changelog.node(rev)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if st == 'add':
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if n not in outnodes:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson seen.append(n)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elif st == 'iter':
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if n in seen:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return rev
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe return self.repo.changelog.rev(node.nullid)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe nodes = set(outgoing)
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ptips = map(lambda x: tipmost_shared(x, nodes), heads)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.repo.changectx(sorted(ptips)[-1])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def status(self, base='.', head=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Translate from the hg 6-tuple status format to a hash keyed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson on change-type'''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson states = ['modified', 'added', 'removed', 'deleted', 'unknown',
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'ignored']
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson chngs = self.repo.status(base, head)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return dict(zip(states, chngs))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def findoutgoing(self, parent):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Return the base set of outgoing nodes.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe A caching wrapper around mercurial.localrepo.findoutgoing().
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe Complains (to the user), if the parent workspace is
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe non-existent or inaccessible'''
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.ui.pushbuffer()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ui = self.ui
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if hasattr(cmdutil, 'remoteui'):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ui = cmdutil.remoteui(ui, {})
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe pws = hg.repository(ui, parent)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return self.repo.findoutgoing(pws)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe except HgRepoError:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.ui.warn("Warning: Parent workspace '%s' is not "
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe "accessible\n"
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe "active list will be incomplete\n\n" % parent)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return []
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.popbuffer()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe findoutgoing = util.cachefunc(findoutgoing)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def modified(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a list of files modified in the workspace'''
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe wctx = self.workingctx()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return sorted(wctx.files() + wctx.deleted()) or None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def merged(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return boolean indicating whether the workspace has an uncommitted
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson merge'''
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe wctx = self.workingctx()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return len(wctx.parents()) > 1
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def branched(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return boolean indicating whether the workspace has an
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson uncommitted named branch'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe wctx = self.workingctx()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return wctx.branch() != wctx.parents()[0].branch()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def active(self, parent=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return an ActiveList describing changes between workspace
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson and parent workspace (including uncommitted changes).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson If workspace has no parent ActiveList will still describe any
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson uncommitted changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = self.parent(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent in self.activecache:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.activecache[parent]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outgoing = self.findoutgoing(parent)
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe outnodes = self.repo.changelog.nodesbetween(outgoing)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outgoing = [] # No parent, no outgoing nodes
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe outnodes = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe localtip = self._localtip(outnodes, self.workingctx())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if localtip.rev() is None:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe heads = localtip.parents()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe heads = [localtip]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ctxs = [self.repo.changectx(n) for n in
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe self.repo.changelog.nodesbetween(outgoing,
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe [h.node() for h in heads])[0]]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if localtip.rev() is None:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ctxs.append(localtip)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe act = ActiveList(self, self._parenttip(heads, outnodes), ctxs)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.activecache[parent] = act
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return act
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
9a70fc3be3b1e966bf78825cdb8d509963a6f0a1Mark J. Nelson def pdiff(self, pats, opts, parent=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'Return diffs relative to PARENT, as best as we can make out'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = self.parent(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson act = self.active(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # act.localtip maybe nil, in the case of uncommitted local
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # changes.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not act.revs:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe matchfunc = cmdutil.match(self.repo, pats, opts)
9a70fc3be3b1e966bf78825cdb8d509963a6f0a1Mark J. Nelson opts = patch.diffopts(self.ui, opts)
9a70fc3be3b1e966bf78825cdb8d509963a6f0a1Mark J. Nelson
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe return self.diff(act.parenttip.node(), act.localtip.node(),
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe match=matchfunc, opts=opts)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def squishdeltas(self, active, message, user=None):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Create a single conglomerate changeset based on a given
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe active list. Removes the original changesets comprising the
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe given active list, and any tags pointing to them.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe Operation:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe - Commit an activectx object representing the specified
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe active list,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe - Remove any local tags pointing to changesets in the
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe specified active list.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe - Remove the changesets comprising the specified active
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe list.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe - Remove any metadata that may refer to changesets that were
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe removed.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe Calling code is expected to hold both the working copy lock
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe and repository lock of the destination workspace
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def strip_local_tags(active):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe '''Remove any local tags referring to the specified nodes.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if os.path.exists(self.repo.join('localtags')):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh = None
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe try:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh = self.repo.opener('localtags')
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe tags = active.prune_tags(fh)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh = self.repo.opener('localtags', 'w', atomictemp=True)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh.writelines(tags)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh.rename()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe finally:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if fh and not fh.closed:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe fh.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if active.files():
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe for entry in active:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # Work around Mercurial issue #1666, if the source
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # file of a rename exists in the working copy
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # Mercurial will complain, and remove the file.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # We preemptively remove the file to avoid the
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # complaint (the user was asked about this in
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # cdm_recommit)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if entry.is_renamed():
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe path = self.repo.wjoin(entry.parentname)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if os.path.exists(path):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe os.unlink(path)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.repo.commitctx(active.context(message, user))
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe wsstate = "recommitted"
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe destination = self.repo.changelog.tip()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If all we're doing is stripping the old nodes, we want to
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # update the working copy such that we're not at a revision
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # that's about to go away.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe wsstate = "tip"
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe destination = active.parenttip.node()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.clean(destination)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # Tags were elided by the activectx object. Local tags,
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # however, must be removed manually.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe try:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe strip_local_tags(active)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe except EnvironmentError, e:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe raise util.Abort('Could not recommit tags: %s\n' % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Silence all the strip and update fun
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.pushbuffer()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # Remove the active lists component changesets by stripping
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # the base of any active branch (of which there may be
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # several)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bases = active.bases()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for basenode in bases:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # Any cached information about the repository is
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # likely to be invalid during the strip. The
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # caching of branch tags is especially
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # problematic.
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe #
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.repo.invalidate()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson repair.strip(self.ui, self.repo, basenode, backup=False)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If this fails, it may leave us in a surprising place in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # the history.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We want to warn the user that something went wrong,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # and what will happen next, re-raise the exception, and
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # bring the working copy back into a consistent state
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (which the finally block will do)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.warn("stripping failed, your workspace will have "
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "superfluous heads.\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "your workspace has been updated to the "
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe "%s changeset.\n" % wsstate)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise # Re-raise the exception
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.clean()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.repo.dirstate.write() # Flush the dirstate
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.repo.invalidate() # Invalidate caches
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We need to remove Hg's undo information (used for rollback),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # since it refers to data that will probably not exist after
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # the strip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.repo.sjoin('undo')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(self.repo.sjoin('undo'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('failed to remove undo data: %s\n' % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.popbuffer()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def filepath(self, path):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'Return the full path to a workspace file.'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.repo.pathto(path)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def clean(self, rev=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Bring workspace up to REV (or tip) forcefully (discarding in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson progress changes)'''
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rev != None:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rev = self.repo.lookup(rev)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rev = self.repo.changelog.tip()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson hg.clean(self.repo, rev, show_stats=False)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def mq_applied(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''True if the workspace has Mq patches applied'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson q = mq.queue(self.ui, self.repo.join(''))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return q.applied
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def workingctx(self):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return self.repo.changectx(None)
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def diff(self, node1=None, node2=None, match=None, opts=None):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ret = cStringIO.StringIO()
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe try:
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe for chunk in patch.diff(self.repo, node1, node2, match=match,
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe opts=opts):
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe ret.write(chunk)
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe finally:
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe # Workaround Hg bug 1651
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe if not Version.at_least("1.3"):
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe self.repo.dirstate.invalidate()
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return ret.getvalue()