Commits

Friedrich Kastner-Masilko committed fa42180

rebranch: added first draft

Comments (0)

Files changed (1)

+# 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)