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#
78add226e8da271dde8f3b5a91d340d1bf010151jmcp# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe# Copyright 2008, 2011, Richard Lowe
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#
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe# The ActiveList, being a list of ActiveEntry objects, can thus
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe# present the entire change in workspace state between a parent and
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe# its child and is the important bit here (in that if it is incorrect,
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe# everything else will be as incorrect, or more)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonimport cStringIO
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonimport os
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowefrom mercurial import cmdutil, context, error, hg, node, patch, repair, util
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom hgext import mq
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowefrom onbld.Scm import Version
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe#
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe# Mercurial 1.6 moves findoutgoing into a discover module
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe#
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Loweif Version.at_least("1.6"):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe from mercurial import discovery
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
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe MODIFIED = intern('modified')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe ADDED = intern('added')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe REMOVED = intern('removed')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def __init__(self, name, change):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.name = name
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.change = intern(change)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe assert change in (self.MODIFIED, self.ADDED, self.REMOVED)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.parentname = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # As opposed to copied (or neither)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.renamed = False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.comments = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __cmp__(self, other):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return cmp(self.name, other.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_added(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return True if this ActiveEntry represents an added file'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self.change is self.ADDED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_modified(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return True if this ActiveEntry represents a modified file'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self.change is self.MODIFIED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_removed(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return True if this ActiveEntry represents a removed file'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self.change is self.REMOVED
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_renamed(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return True if this ActiveEntry represents a renamed file'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.parentname and self.renamed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def is_copied(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return True if this ActiveEntry represents a copied file'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.parentname and not self.renamed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass ActiveList(object):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Complete representation of change between two changesets.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe In practice, a container for ActiveEntry objects, and methods to
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe create them, and deal with them as a group.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, ws, parenttip, revs=None):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Initialize the ActiveList
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe parenttip is the revision with which to compare (likely to be
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe from the parent), revs is a topologically sorted list of
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe revisions ending with the revision to compare with (likely to
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe be the child-local revisions).'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe assert parenttip is not None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.ws = ws
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.revs = revs
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.parenttip = parenttip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.localtip = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._active = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self._comments = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if revs:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.localtip = revs[-1]
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._build()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def _status(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return the status of any file mentioned in any of the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe changesets making up this active list.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe files = set()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for c in self.revs:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe files.update(c.files())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Any file not in the parenttip or the localtip is ephemeral
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # and can be ignored. Mercurial will complain regarding these
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # files if the localtip is a workingctx, so remove them in
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # that case.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Compare against the dirstate because a workingctx manifest
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # is created on-demand and is particularly expensive.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if self.localtip.rev() is None:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for f in files.copy():
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if f not in self.parenttip and f not in self.ws.repo.dirstate:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe files.remove(f)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self.ws.status(self.parenttip, self.localtip, files=files)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def _build(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Construct ActiveEntry objects for each changed file.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe This works in 3 stages:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe - Create entries for every changed file with
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe semi-appropriate change type
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe - Track renames/copies, and set change comments (both
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe ActiveList-wide, and per-file).
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe - Cleanup
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe - Drop circular renames
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe - Drop the removal of the old name of any rename
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe - Drop entries for modified files that haven't actually changed'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Keep a cache of filectx objects (keyed on pathname) so that
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # we can avoid opening filelogs numerous times.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fctxcache = {}
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def oldname(ctx, fname):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return the name 'fname' held prior to any possible
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe rename/copy in the given changeset.'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe try:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if fname in fctxcache:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe octx = fctxcache[fname]
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fctx = ctx.filectx(fname, filelog=octx.filelog())
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe else:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fctx = ctx.filectx(fname)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # workingfilectx objects may not refer to the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # right filelog (in case of rename). Don't cache
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # them.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if not isinstance(fctx, context.workingfilectx):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fctxcache[fname] = fctx
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe except error.LookupError:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe rn = fctx.renamed()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return rn and rn[0] or fname
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe status = self._status()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._active = dict((fname, ActiveEntry(fname, kind))
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for fname, kind in status.iteritems()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if kind in ('modified', 'added', 'removed'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # We do two things:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # - Gather checkin comments (for the entire ActiveList, and
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # per-file)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # - Set the .parentname of any copied/renamed file
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # renames/copies:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # We walk the list of revisions backward such that only files
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # that ultimately remain active need be considered.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # At each iteration (revision) we update the .parentname of
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # any active file renamed or copied in that revision (the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # current .parentname if set, or .name otherwise, reflects
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # the name of a given active file in the revision currently
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # being looked at)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for ctx in reversed(self.revs):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe desc = ctx.description().splitlines()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._comments = desc + self._comments
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cfiles = set(ctx.files())
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for entry in self:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fname = entry.parentname or entry.name
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if fname not in cfiles:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe continue
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe entry.comments = desc + entry.comments
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # We don't care about the name history of any file
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # that ends up being removed, since that trumps any
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # possible renames or copies along the way.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Changes that we may care about involving an
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # intermediate name of a removed file will appear
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # separately (related to the eventual name along
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # that line)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if not entry.is_removed():
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe entry.parentname = oldname(ctx, fname)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for entry in self._active.values():
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # For any file marked as copied or renamed, clear the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # .parentname if the copy or rename is cyclic (source ==
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # destination) or if the .parentname did not exist in the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # parenttip.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # If the parentname is marked as removed, set the renamed
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # flag and remove any ActiveEntry we may have for the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # .parentname.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if entry.parentname:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if (entry.parentname == entry.name or
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe entry.parentname not in self.parenttip):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe entry.parentname = None
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe elif status.get(entry.parentname) == 'removed':
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe entry.renamed = True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if entry.parentname in self:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe del self[entry.parentname]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # There are cases during a merge where a file will be seen
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # as modified by status but in reality be an addition (not
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # in the parenttip), so we have to check whether the file
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # is in the parenttip and set it as an addition, if not.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # If a file is modified (and not a copy or rename), we do
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # a full comparison to the copy in the parenttip and
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # ignore files that are parts of active revisions but
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # unchanged.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if entry.name not in self.parenttip:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson entry.change = ActiveEntry.ADDED
78add226e8da271dde8f3b5a91d340d1bf010151jmcp elif entry.is_modified():
78add226e8da271dde8f3b5a91d340d1bf010151jmcp if not self._changed_file(entry.name):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson del self[entry.name]
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):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self._active.itervalues()
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()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe ret.extend(x.parentname for x in self if x.is_renamed())
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return set(ret)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def comments(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return the full set of changeset comments associated with
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe this ActiveList'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self._comments
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def bases(self):
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp '''Return the list of changesets that are roots of the ActiveList.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp This is the set of active changesets where neither parent
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp changeset is itself active.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp revset = set(self.revs)
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp return filter(lambda ctx: not [p for p in ctx.parents() if p in revset],
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp self.revs)
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
78add226e8da271dde8f3b5a91d340d1bf010151jmcp def _changed_file(self, path):
78add226e8da271dde8f3b5a91d340d1bf010151jmcp '''Compare the parent and local versions of a given file.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Return True if file changed, False otherwise.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
78add226e8da271dde8f3b5a91d340d1bf010151jmcp Note that this compares the given path in both versions, not the given
78add226e8da271dde8f3b5a91d340d1bf010151jmcp entry; renamed and copied files are compared by name, not history.
78add226e8da271dde8f3b5a91d340d1bf010151jmcp
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson The fast path compares file metadata, slow path is a
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson real comparison of file content.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if ((path in self.parenttip) != (path in self.localtip)):
78add226e8da271dde8f3b5a91d340d1bf010151jmcp return True
78add226e8da271dde8f3b5a91d340d1bf010151jmcp
78add226e8da271dde8f3b5a91d340d1bf010151jmcp parentfile = self.parenttip.filectx(path)
78add226e8da271dde8f3b5a91d340d1bf010151jmcp localfile = self.localtip.filectx(path)
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
036abaca93ddab92ba33036159c30112ab844810Richard Lowe if Version.at_least("1.7"):
036abaca93ddab92ba33036159c30112ab844810Richard Lowe if parentfile.cmp(localfile):
036abaca93ddab92ba33036159c30112ab844810Richard Lowe return True
036abaca93ddab92ba33036159c30112ab844810Richard Lowe else:
036abaca93ddab92ba33036159c30112ab844810Richard Lowe if parentfile.cmp(localfile.data()):
036abaca93ddab92ba33036159c30112ab844810Richard Lowe 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
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def as_text(self, paths):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return the ActiveList as a block of text in a format
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe intended to aid debugging and simplify the test suite.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe paths should be a list of paths for which file-level data
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe should be included. If it is empty, the whole active list is
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe included.'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr = cStringIO.StringIO()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write('parent tip: %s:%s\n' % (self.parenttip.rev(),
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.parenttip))
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if self.localtip:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe rev = self.localtip.rev()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write('local tip: %s:%s\n' %
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe (rev is None and "working" or rev, self.localtip))
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe else:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write('local tip: None\n')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write('entries:\n')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for entry in self:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if paths and self.ws.filepath(entry.name) not in paths:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe continue
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write(' - %s\n' % entry.name)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write(' parentname: %s\n' % entry.parentname)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write(' change: %s\n' % entry.change)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write(' renamed: %s\n' % entry.renamed)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write(' comments:\n')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write(' ' + '\n '.join(entry.comments) + '\n')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cstr.write('\n')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return cstr.getvalue()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Loweclass WorkList(object):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''A (user-maintained) list of files changed in this workspace as
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe compared to any parent workspace.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe Internally, the WorkList is stored in .hg/cdm/worklist as a list
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe of file pathnames, one per-line.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe This may only safely be used as a hint regarding possible
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe modifications to the working copy, it should not be relied upon to
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe suggest anything about committed changes.'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def __init__(self, ws):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Load the WorkList for the specified WorkSpace from disk.'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._ws = ws
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._repo = ws.repo
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._file = os.path.join('cdm', 'worklist')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._files = set()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._valid = False
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if os.path.exists(self._repo.join(self._file)):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.load()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def __nonzero__(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''A WorkList object is true if it was loaded from disk,
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe rather than freshly created.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self._valid
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def list(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''List of pathnames contained in the WorkList
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return list(self._files)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def status(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return the status (in tuple form) of files from the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe WorkList as they are in the working copy
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe match = self._ws.matcher(files=self.list())
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self._repo.status(match=match)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def add(self, fname):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Add FNAME to the WorkList.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._files.add(fname)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def write(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Write the WorkList out to disk.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe dirn = os.path.split(self._file)[0]
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if dirn and not os.path.exists(self._repo.join(dirn)):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe try:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe os.makedirs(self._repo.join(dirn))
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe except EnvironmentError, e:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe raise util.Abort("Couldn't create directory %s: %s" %
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe (self._repo.join(dirn), e))
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fh = self._repo.opener(self._file, 'w', atomictemp=True)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for name in self._files:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fh.write("%s\n" % name)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fh.rename()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fh.close()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def load(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Read in the WorkList from disk.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fh = self._repo.opener(self._file, 'r')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._files = set(l.rstrip('\n') for l in fh)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._valid = True
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe fh.close()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def delete(self):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Empty the WorkList
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe Remove the on-disk WorkList and clear the file-list of the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe in-memory copy
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if os.path.exists(self._repo.join(self._file)):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe os.unlink(self._repo.join(self._file))
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._files = set()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self._valid = False
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard 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):
bb664d7ba7a656b21763d44dfc2da581c11444afRichard Lowe ret = set(entry.name for entry in self.__active if entry.is_removed())
bb664d7ba7a656b21763d44dfc2da581c11444afRichard Lowe ret.update(set(x.parentname for x in self.__active if x.is_renamed()))
bb664d7ba7a656b21763d44dfc2da581c11444afRichard Lowe return list(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 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
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard 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
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe This returns the most recent changeset on a given branch that
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe is shared between a parent and child workspace, in effect the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe common ancestor of the chosen local tip and the parent
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe workspace.
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe '''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def tipmost_shared(head, outnodes):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''Return the changeset on the same branch as head that is
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe not in outnodes and is closest to the tip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe Walk outgoing changesets from head to the bottom of the
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe workspace (revision 0) and return the the first changeset
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe we see that is not in outnodes.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe If none 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.
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe for ctx in self._walkctxs(head, self.repo.changectx(0),
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe follow=True,
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe pick=lambda c: c.node() not in outnodes):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe return ctx
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe return self.repo.changectx(node.nullid)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe nodes = set(outgoing)
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ptips = map(lambda x: tipmost_shared(x, nodes), heads)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe return sorted(ptips, key=lambda x: x.rev(), reverse=True)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def status(self, base='.', head=None, files=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',
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe 'ignored']
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe match = self.matcher(files=files)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe chngs = self.repo.status(base, head, match=match)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe ret = {}
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for paths, change in zip(chngs, states):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe ret.update((f, change) for f in paths)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return ret
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)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if Version.at_least("1.6"):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe return discovery.findoutgoing(self.repo, pws)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe else:
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe return self.repo.findoutgoing(pws)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe except error.RepoError:
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'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
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'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
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
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def active(self, parent=None, thorough=False):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return an ActiveList describing changes between workspace
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson and parent workspace (including uncommitted changes).
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe If the workspace has no parent, ActiveList will still describe any
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe uncommitted changes.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe If thorough is True use neither the WorkList nor any cached
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe results (though the result of this call will be cached for
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe future, non-thorough, calls).'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = self.parent(parent)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Use the cached copy if we can (we have one, and weren't
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # asked to be thorough)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if not thorough and parent in self.activecache:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.activecache[parent]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # outbases: The set of outgoing nodes with no outgoing ancestors
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # outnodes: The full set of outgoing nodes
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe outbases = self.findoutgoing(parent)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe outnodes = self.repo.changelog.nodesbetween(outbases)[0]
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe else: # No parent, no outgoing nodes
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe outbases = []
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe outnodes = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe wctx = self.workingctx(worklist=not thorough)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe localtip = self._localtip(outnodes, wctx)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if localtip.rev() is None:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe heads = localtip.parents()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe heads = [localtip]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe parenttip = self.parenttip(heads, outnodes)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # If we couldn't find a parenttip, the two repositories must
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # be unrelated (Hg catches most of this, but this case is
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # valid for it but invalid for us)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if parenttip == None:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe raise util.Abort('repository is unrelated')
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe headnodes = [h.node() for h in heads]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ctxs = [self.repo.changectx(n) for n in
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe self.repo.changelog.nodesbetween(outbases, headnodes)[0]]
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe if localtip.rev() is None:
0df7087fda4bb16f7e1cf07d1b90fcf070c19484Richard Lowe ctxs.append(localtip)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe act = ActiveList(self, parenttip, ctxs)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.activecache[parent] = act
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return act
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 #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Remove the previous child-local changes by stripping the
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # nodes that form the base of the ActiveList (removing their
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # children in the process).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp for base in active.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()
c7f512e49da83ae2cd3d4b339e1a8366544471e2jmcp repair.strip(self.ui, self.repo, base.node(), 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.'
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
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'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson q = mq.queue(self.ui, self.repo.join(''))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return q.applied
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def workingctx(self, worklist=False):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return a workingctx object representing the working copy.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe If worklist is true, return a workingctx object created based
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe on the status of files in the workspace's worklist.'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe wl = WorkList(self)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if worklist and wl:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return context.workingctx(self.repo, changes=wl.status())
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe else:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return self.repo.changectx(None)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe def matcher(self, pats=None, opts=None, files=None):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return a match object suitable for Mercurial based on
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe specified criteria.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe If files is specified it is a list of pathnames relative to
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe the repository root to be matched precisely.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe If pats and/or opts are specified, these are as to
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe cmdutil.match'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe of_patterns = pats is not None or opts is not None
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe of_files = files is not None
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe opts = opts or {} # must be a dict
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe assert not (of_patterns and of_files)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if of_patterns:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return cmdutil.match(self.repo, pats, opts)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe elif of_files:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return cmdutil.matchfiles(self.repo, files)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe else:
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe return cmdutil.matchall(self.repo)
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe def diff(self, node1=None, node2=None, match=None, opts=None):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe '''Return the diff of changes between two changesets as a string'''
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # Retain compatibility by only calling diffopts() if it
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe # obviously has not already been done.
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe #
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe if isinstance(opts, dict):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe opts = patch.diffopts(self.ui, opts)
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe ret = cStringIO.StringIO()
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe for chunk in patch.diff(self.repo, node1, node2, match=match,
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe opts=opts):
605a716e6d38b3af09034c254382d0ae3b7d5f70Richard Lowe ret.write(chunk)
2b5878de2735cb61d008168e1f27e390d2edf915Rich Lowe
c959a081a8aebb76386b6d8ea3afa850e328f6c7Richard Lowe return ret.getvalue()
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if Version.at_least("1.6"):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe def copy(self, src, dest):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''Copy a file from src to dest
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe self.workingctx().copy(src, dest)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe else:
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe def copy(self, src, dest):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''Copy a file from src to dest
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe self.repo.copy(src, dest)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if Version.at_least("1.4"):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe def _walkctxs(self, base, head, follow=False, pick=None):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''Generate changectxs between BASE and HEAD.
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe Walk changesets between BASE and HEAD (in the order implied by
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe their relation), following a given branch if FOLLOW is a true
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe value, yielding changectxs where PICK (if specified) returns a
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe true value.
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe PICK is a function of one argument, a changectx.'''
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe chosen = {}
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe def prep(ctx, fns):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe chosen[ctx.rev()] = not pick or pick(ctx)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe opts = {'rev': ['%s:%s' % (base.rev(), head.rev())],
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe 'follow': follow}
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe matcher = cmdutil.matchall(self.repo)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe for ctx in cmdutil.walkchangerevs(self.repo, matcher, opts, prep):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if chosen[ctx.rev()]:
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe yield ctx
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe else:
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe def _walkctxs(self, base, head, follow=False, pick=None):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe '''Generate changectxs between BASE and HEAD.
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe Walk changesets between BASE and HEAD (in the order implied by
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe their relation), following a given branch if FOLLOW is a true
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe value, yielding changectxs where PICK (if specified) returns a
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe true value.
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe PICK is a function of one argument, a changectx.'''
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe opts = {'rev': ['%s:%s' % (base.rev(), head.rev())],
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe 'follow': follow}
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe changectx = self.repo.changectx
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe getcset = util.cachefunc(lambda r: changectx(r).changeset())
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe #
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe # See the docstring of mercurial.cmdutil.walkchangerevs() for
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe # the phased approach to the iterator returned. The important
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe # part to note is that the 'add' phase gathers nodes, which
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe # the 'iter' phase then iterates through.
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe #
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe changeiter = cmdutil.walkchangerevs(self.ui, self.repo,
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe [], getcset, opts)[0]
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe matched = {}
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe for st, rev, fns in changeiter:
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if st == 'add':
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe ctx = changectx(rev)
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if not pick or pick(ctx):
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe matched[rev] = ctx
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe elif st == 'iter':
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe if rev in matched:
8703921742d9c7d4d3724f89a39ff0e2725cbe7bRichard Lowe yield matched[rev]