WorkSpace.py revision cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0
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# 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# 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# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Use is subject to license terms.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# ident "%Z%%M% %I% %E% SMI"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Workspaces have a non-binding parent/child relationship.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# All important operations apply to the changes between the two.
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# 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# 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)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom mercurial import hg, patch, cmdutil, util, node, repo
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Representation of the changes made to a single file.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson MODIFIED - Contents changed, but no other changes were made
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ADDED - File is newly created
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson REMOVED - File is being removed
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Copies are represented by an Entry whose .parentname is non-nil
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Truly copied files have non-nil .parentname and .renamed = False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Renames have non-nil .parentname and .renamed = True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Do not access any of this information directly, do so via the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson .is_<change>() methods.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # As opposed to copied (or neither)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # ActiveEntrys sort by the name of the file they represent.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Complete representation of workspace change.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson In practice, a container for ActiveEntrys, and methods to build them,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson update them, and deal with them en masse.'''
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 status = self.ws.status(self.parenttip.node(), self.localtip.node())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # When a file is renamed, two operations actually occur.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # A file copy from source to dest and a removal of source.
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 # Since these are unconnected in both the context and
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # status we can only make the association by explicitly
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # looking for it.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We deal with this thusly:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We maintain a dict dest -> source of all copies
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (updating dest as appropriate, but leaving source alone).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # After all other processing, we mark as renamed any pair
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # where source is on the removed list.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Walk revs looking for renames and adding files that
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # are in both change context and status to the active
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 # NB: .renamed() is a misnomer, this actually checks
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # for copies.
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 # We detect cycles at a later point. There is no
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # reason to continuously handle them.
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 # Where source is removed, mark as renamed, and remove the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # AL entry for the source file
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fname == oldname or fname not in self.localtip.manifest():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Walk the active list setting the change type for each active
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 # We walk a copy of the AL such that we can drop entries
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # within the loop.
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 # We need to check whether the file is actually present
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # in the parenttip, and set it as an add, if not.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Return list of files represented in this AL,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # including the parent file of a rename
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 # Immediately after recommit, this leaves us looking like this:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # * <- recommitted changeset (real tip)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | * <- Local tip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # * | <- parent tip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # | * | | <- Base
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # [left-most is parent, next is child, right two being
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # branches in child, intermediate merges parent->child
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 # 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 # 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 '''Find the bases that in combination define the "old"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson side of a recommitted set of changes, based on AL'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson get = util.cachefunc(lambda r: self.ws.repo.changectx(r).changeset())
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 changeiter = cmdutil.walkchangerevs(self.ws.repo.ui, self.ws.repo,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if n not in hold:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self.ws.repo.changectx(p[0]).rev() not in alrevs:
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 We return all instances of a tag that refer to such a node,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson not just that which takes precedence.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We want to use the tags file from the localtip
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rtags = [x.rstrip().split(' ') for x in f.data().splitlines()]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tags.append((name, self.ws.repo.lookup(nd), False))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Compare two revisions of two files
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Return True if file changed, False otherwise.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson The fast path compares file metadata, slow path is a
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson real comparison of file content.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parentfile = self.parenttip.filectx(entry.parentname or entry.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # NB: Keep these ordered such as to make every attempt
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # to short-circuit the more time consuming checks.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parentfile.fileflags() != localfile.fileflags():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return canonical workspace parent, either SPEC if passed,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson or default parent otherwise'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a tuple (changectx, workingctx) representing the most
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson representative head to act as the local tip.
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 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 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 return sorted(nodes, cmp=lambda x, y: cmp(x.rev(), y.rev()))[-1]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We need a full set of outgoing nodes such that we can limit
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # local branch heads to those which are outgoing
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outnodes = self.repo.changelog.nodesbetween(bases, heads)[0]
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 # (and the working head is it)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson localchoices = [n for n in heads if n.node() in outnodes]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Find the closest approximation of the parents tip, as best
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson In parent-less workspaces returns our tip (given the best
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson we can do is deal with uncommitted changes)'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return the tipmost node on the same branch as head that is not
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson in outnodes.
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 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 get = util.cachefunc(lambda r: self.repo.changectx(r).changeset())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson changeiter = cmdutil.walkchangerevs(self.repo.ui, self.repo, [],
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if n not in outnodes:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson nodes = self.repo.changelog.nodesbetween(outgoing)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ptips = map(lambda x: tipmost_shared(x.rev(), nodes), possible_branches)
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 # Cache findoutgoing results
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "Warning: Parent workspace %s is not accessible\n" % parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a list of files modified in the workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return sorted(wctx.files() + wctx.deleted()) or None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return boolean indicating whether the workspace has an uncommitted
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return boolean indicating whether the workspace has an
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson uncommitted named branch'''
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 branchheads = self.repo.heads(start=self.repo.dirstate.parents()[0])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ourhead, workinghead = self._localtip(outgoing, branchheads)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui.warn('The current branch has more than one head, '
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson [h.node() for h in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson act = ActiveList(self, self._parenttip((ourhead, workinghead), parent),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'Return diffs relative to PARENT, as best as we can make out'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # act.localtip maybe nil, in the case of uncommitted local
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson patch.diff(self.repo, act.parenttip.node(), act.localtip.node(),
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 # We achieve this in a somewhat confusing fashion.
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 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 The old changes are removed.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Remove any tags referring to the specified nodes.'''
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 = 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 # 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 # The easiest way to achieve this is to update the working
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # copy to tip.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson strip_tags([node.hex(ctx.node()) for ctx in active.revs])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Could not recommit tags: %s\n' % e)
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 if not entry.is_renamed() and not entry.is_copied():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ("parentname '%s' (of '%s') not in parent" %
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 # 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 os.path.exists(self.repo.wjoin(entry.parentname))):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.repo.commit(files=active.files(), text=message,
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 # Silence all the strip and update fun
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 repair.strip(self.ui, self.repo, basenode, backup=False)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If this fails, it may leave us in a surprising place in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # the history.
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 self.ui.warn("stripping failed, your workspace will have "
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "superfluous heads.\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "your workspace has been updated to the "
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise # Re-raise the exception
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 raise util.Abort('failed to remove undo data: %s\n' % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'Return the full path to a workspace file.'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Bring workspace up to REV (or tip) forcefully (discarding in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson progress changes)'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rev != None:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''True if the workspace has Mq patches applied'''