WorkSpace.py revision cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0
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#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Use is subject to license terms.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# ident "%Z%%M% %I% %E% SMI"
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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom mercurial import hg, patch, cmdutil, util, node, repo
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom mercurial import revlog, repair
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom hgext import mq
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
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)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except revlog.LookupError:
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 #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Return list of files represented in this AL,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # including the parent file of a rename
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def files(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = self._active.keys()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret.extend([x.parentname for x in self
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if x.parentname 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,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson returning a list of 3-tuples (tag, node, is_local) for each.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson We return all instances of a tag that refer to such a node,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson not just that which takes precedence.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.ws.repo.join('localtags')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson l = self.ws.repo.opener('localtags').readlines()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ltags = [x.rstrip().split(' ') for x in l]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ltags = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We want to use the tags file from the localtip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if '.hgtags' in self.localtip.manifest():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f = self.localtip.filectx('.hgtags')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rtags = [x.rstrip().split(' ') for x in f.data().splitlines()]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rtags = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson nodes = [node.hex(n.node()) for n in self.revs]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for nd, name in rtags:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if nd in nodes:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags.append((name, self.ws.repo.lookup(nd), False))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for nd, name in ltags:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if nd in nodes:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags.append((name, self.ws.repo.lookup(nd), True))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return tags
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parentfile.fileflags() != localfile.fileflags():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parentfile.cmp(localfile.data()):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
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 parent = self.repo.ui.expandpath('default')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent == 'default':
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.parentrepo = parent
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.activecache = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.outgoingcache = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def parent(self, spec=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return canonical workspace parent, either SPEC if passed,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson or default parent otherwise'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return spec or self.parentrepo
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _localtip(self, bases, heads):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a tuple (changectx, workingctx) representing the most
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson representative head to act as the local tip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson If the working directory is modified, the changectx is its
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tipmost local parent (or tipmost parent, if neither is
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson local), and the workingctx is non-null.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson If the working directory is clean, the workingctx is null.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson The changectx is the tip-most local head on the current branch.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson If this can't be determined for some reason (e.g., the parent
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson repo is inacessible), changectx is the tip-most head on the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson current branch.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson If the workingctx is non-null it is the actual local tip (and would
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson be the local tip in any generated ActiveList, for instance),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson the better parent revision is returned also to aid callers needing
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson a real changeset to act as a surrogate for an uncommitted change.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def tipmost_of(nodes):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return sorted(nodes, cmp=lambda x, y: cmp(x.rev(), y.rev()))[-1]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We need a full set of outgoing nodes such that we can limit
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # local branch heads to those which are outgoing
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outnodes = self.repo.changelog.nodesbetween(bases, heads)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx = self.repo.workingctx()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # A modified working context is seen as a proto-branch, where
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # the 'heads' from our view are the parent revisions of that
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # context.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (and the working head is it)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (wctx.files() or len(wctx.parents()) > 1 or
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx.branch() != wctx.parents()[0].branch()):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson heads = wctx.parents()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson heads = [self.repo.changectx(n) for n in heads]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson localchoices = [n for n in heads if n.node() in outnodes]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return (tipmost_of(localchoices or heads), wctx)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _parenttip(self, localtip, parent=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Find the closest approximation of the parents tip, as best
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson as we can.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson In parent-less workspaces returns our tip (given the best
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson we can do is deal with uncommitted changes)'''
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
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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson get = util.cachefunc(lambda r: self.repo.changectx(r).changeset())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson changeiter = cmdutil.walkchangerevs(self.repo.ui, self.repo, [],
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson get, {'rev': ['%s:0' % head],
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'follow': True})[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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tipctx, wctx = localtip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = self.parent(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outgoing = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outgoing = self.findoutgoing(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if wctx:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson possible_branches = wctx.parents()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson possible_branches = [tipctx]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson nodes = self.repo.changelog.nodesbetween(outgoing)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ptips = map(lambda x: tipmost_shared(x.rev(), nodes), possible_branches)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return self.repo.changectx(sorted(ptips)[-1])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def status(self, base=None, head=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Translate from the hg 6-tuple status format to a hash keyed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson on change-type'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson states = ['modified', 'added', 'removed', 'deleted', 'unknown',
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'ignored']
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson chngs = self.repo.status(base, head)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return dict(zip(states, chngs))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Cache findoutgoing results
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def findoutgoing(self, parent):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent in self.outgoingcache:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = self.outgoingcache[parent]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.pushbuffer()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson pws = hg.repository(self.ui, parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = self.repo.findoutgoing(pws)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except repo.RepoError:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.warn(
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "Warning: Parent workspace %s is not accessible\n" % parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.warn("active list will be incomplete\n\n")
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.outgoingcache[parent] = ret
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.popbuffer()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return ret
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def modified(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a list of files modified in the workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx = self.repo.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'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx = self.repo.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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx = self.repo.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)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outgoing = [] # No parent, no outgoing nodes
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson branchheads = self.repo.heads(start=self.repo.dirstate.parents()[0])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ourhead, workinghead = self._localtip(outgoing, branchheads)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if len(branchheads) > 1:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.warn('The current branch has more than one head, '
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'using %s\n' % ourhead.rev())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if workinghead:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parents = workinghead.parents()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ctxs = [self.repo.changectx(n) for n in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.changelog.nodesbetween(outgoing,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson [h.node() for h in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parents])[0]]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ctxs.append(workinghead)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ctxs = [self.repo.changectx(n) for n in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.changelog.nodesbetween(outgoing,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson [ourhead.node()])[0]]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson act = ActiveList(self, self._parenttip((ourhead, workinghead), parent),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ctxs)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.activecache[parent] = act
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return act
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def pdiff(self, 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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = cStringIO.StringIO()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson patch.diff(self.repo, act.parenttip.node(), act.localtip.node(),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp=ret)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return ret.getvalue()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Theory:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We wish to go from a single series of consecutive changesets
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (possibly including merges with the parent) to a single
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # changeset faithfully representing contents and copy history.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We achieve this in a somewhat confusing fashion.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # - Sanity check the workspace
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # - Update the workspace to tip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # - Enter into the dirstate the sum total of file contents in the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # to-be-squished changesets
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # - Commit this in-progress change (which has no changes at all,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # in reality) On top of the effective parent tip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # - Strip the child-local branch(es) (see ActiveList.bases())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def squishdeltas(self, active, message, user=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Create a single conglomerate changeset, with log message MESSAGE
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson containing the changes from ACTIVE. USER, if set, is used
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson as the author name.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson The old changes are removed.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def strip_tags(nodes):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Remove any tags referring to the specified nodes.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.repo.join('localtags')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh = self.repo.opener('localtags').readlines()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags = [t for t in fh if t.split(' ')[0] not in nodes]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh = self.repo.opener('localtags', 'w', atomictemp=True)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh.writelines(tags)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh.rename()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.repo.wjoin('.hgtags')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh = self.repo.wopener('.hgtags', 'rb').readlines()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags = [t for t in fh if t.split(' ')[0] not in nodes]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh = self.repo.wopener('.hgtags', 'wb', atomictemp=True)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh.writelines(tags)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fh.rename()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wlock = self.repo.wlock()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson lock = self.repo.lock()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # The files involved need to be present in the workspace and
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # not otherwise molested, rather than the workspace not being
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # modified we also need to prevent files being deleted (but
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # left versioned) too.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # The easiest way to achieve this is to update the working
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # copy to tip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.clean()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson strip_tags([node.hex(ctx.node()) for ctx in active.revs])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Could not recommit tags: %s\n' % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # For copied files, we need to enter the copy into the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # dirstate before we force the commit such that the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # file logs of both branches (old and new) contain
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # representation of the copy.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parentman = active.parenttip.manifest()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for entry in active:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not entry.is_renamed() and not entry.is_copied():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson assert entry.parentname in parentman, \
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ("parentname '%s' (of '%s') not in parent" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (entry.parentname, entry.name))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If the source file exists, and used to be versioned
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # this will cause this to become a true copy
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (re-introducing the source file)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We bandaid this, by removing the source file in this
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # case. If we're here, the user has already agreed to this
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # from above.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (entry.is_renamed() and
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.path.exists(self.repo.wjoin(entry.parentname))):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(self.repo.wjoin(entry.parentname))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.copy(entry.parentname, entry.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if active.files():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson extra = {'branch': active.localtip.branch()}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.commit(files=active.files(), text=message,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson user=user, p1=active.parenttip.node(), p2=None,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson extra=extra)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wsstate = "recommitted changeset"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.clean()
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 #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wsstate = "tip changeset"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.clean(rev=active.parenttip.node())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Silence all the strip and update fun
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.pushbuffer()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We must strip away the old representation of the child
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # branch(es). This may involve stripping a theoretically
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # large number of branches in certain cases
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bases = active.bases()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for basenode in bases:
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 "
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "%s.\n" % wsstate)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise # Re-raise the exception
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
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
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.clean()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.dirstate.write() # Flush the dirstate
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.invalidate() # Invalidate caches
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)'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rev != None:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rev = self.repo.lookup(rev)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rev = self.repo.changelog.tip()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wlock = self.repo.wlock()
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