Source

timestamp / rebranch.py

# rebranch.py - node recoloring similar to global tags
#
# Copyright 2012 Friedrich Kastner-Masilko <face@snoopie.at>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
#
'''node recoloring similar to global tags

This extension basically lets you rename named branches as well as create new named branches after commits already happened.
'''

from mercurial import cmdutil, localrepo, context, encoding, util
from mercurial.i18n import _
propertycache = util.propertycache

cmdtable = {}
command = cmdutil.command(cmdtable)

#def override_pull(orig, ui, repo, source=None, **opts):
#    result = orig(ui, repo, source, **opts)
#    pull(ui, databasepath(ui), readconfig(ui)[0])
#    return result

def findglobalrebranches(ui, self, allrebranches):
    allrebranches.extend([((self.changelog.node(0), self.changelog.tip()), "default")])

def readlocalrebranches(ui, self, allrebranches):
    allrebranches.extend([((self.changelog.node(0), self.changelog.node(0)), "myroot")])

@command('^rebranch',
         [('r', 'rev', None, _('revision set by range specification'), _('REV')),
          ('b', 'branch', None, _('revision set by branch name'), _('BRANCH')),
          ('d', 'delete', None, _('deletes specified branch')),
         ],
         _('hg rebranch [-r REV | -b BRANCH | -d] BRANCH'))
def rebranch(ui, repo, newbranch=None, **opts):
    '''rebranches a given revision set to the specified branch name

    If no option is specified, all revisions in the current branch will
    get the new branch name. If -r is given, the revsets determines the
    revisions for the new branch, with -b, the given branch name addresses
    them. With the -d option, all nodes addressed with the branch name will
    get the branch of their ancestors, thus effectively deleting the branch
    name.

    This command will affect the current working copy's branch name, if a
    parent on the same branch is affected by the operation. I.e. if your
    current working copy is "mybranch", and the second parent is "mybranch",
    and you change "mybranch" to "newbranch", the working copy will be also
    marked as "newbranch".

    Like with the tag command, this command commits the changes to the
    .hgrebranch file.
    '''
    return

def reposetup(ui, repo):
    # temporarly force file cache deletion
    #repo.destroyed()
    pass

def uisetup(ui):
    # Install new functions in localrepo class

    def nodebranch(self, node, default):
        '''return the branch name associated with a node'''
        if not self._rebranchescache.nodebranchcache:
            nodebranchcache = {}
            branchcache = []

            c = self.changelog

            for rule, name in self._rebranchescache.rebranches:
                if name not in branchcache:
                    branchcache.append(name)
                bindex=branchcache.index(name)
                start = self[rule[0]].rev()
                visit = [self[rule[1]].rev()]
                reachable = {visit[0]:[]}
                while visit:
                    n = visit.pop(0)
                    if n == start:
                        continue
                    if n < 0:
                        continue
                    for p in c.parentrevs(n):
                        if p < start:
                            continue
                        if p not in reachable:
                            reachable[p]=[n]
                            visit.append(p)
                        else:
                            reachable[p].append(n)
                nodebranchcache[start]=bindex
                if start in reachable:                    
                    visit = reachable[start]
                    while visit:
                        n = visit.pop(0)                        
                        if n in reachable and n not in nodebranchcache:
                            nodebranchcache[n]=bindex
                            visit.extend(reachable[n])
                    
            self._rebranchescache.nodebranchcache = nodebranchcache
            self._rebranchescache.branchcache = branchcache
            
        rev = self[node].rev()
        if rev in self._rebranchescache.nodebranchcache:
            return encoding.tolocal(self._rebranchescache.branchcache[self._rebranchescache.nodebranchcache[rev]])

        if default not in self._rebranchescache.branchcache:
            self._rebranchescache.branchcache.append(default)
        self._rebranchescache.nodebranchcache[rev]=self._rebranchescache.branchcache.index(default)
        return encoding.tolocal(default)
    localrepo.localrepository.nodebranch=nodebranch

    @propertycache
    def _rebranchescache(self):
        '''Returns a rebranchescache object that contains various rebranches related caches.'''

        # This simplifies its cache management by having one decorated
        # function (this one) and the rest simply fetch things from it.
        class rebranchescache(object):
            def __init__(self):
                # These two define the list of rebranches for this repository.
                # rebranches lists rule-to-name tuples;
                self.rebranches = None
                self.nodebranchcache = None
                self.branchcache = None

        cache = rebranchescache()
        cache.rebranches = self._findrebranches()

        return cache
    localrepo.localrepository._rebranchescache=_rebranchescache

    def _findrebranches(self):
        '''Do the hard work of finding rebranches. Return a list of
           rule/name tuples, with rule being a pair of binary nodes'''

        allrebranches = []

        findglobalrebranches(self.ui, self, allrebranches)
        readlocalrebranches(self.ui, self, allrebranches)

        return allrebranches
    localrepo.localrepository._findrebranches=_findrebranches

    # Overwrite changectx.branch(self) to call nodebranch function
    def branch(self):
        return self._repo.nodebranch(self._node, self._changeset[5].get("branch"))
    context.changectx.branch=branch
    #extensions.wrapcommand(commands.table, 'pull', override_pull)