Add "private" concept (2). private changeset are hiden suppose to be hidden for
authorPierre-Yves David <pierre-yves.david@logilab.fr>
Fri, 20 May 2011 19:14:19 +0200
changeset 1 beabde937e36
parent 0 bbeef801409c
child 2 166694e62daf
Add "private" concept (2). private changeset are hiden suppose to be hidden for pull and push. For now it works for out and push
states.py
tests/test-state.t
--- a/states.py	Fri May 20 16:16:34 2011 +0200
+++ b/states.py	Fri May 20 19:14:19 2011 +0200
@@ -17,22 +17,39 @@
 
 name are not fixed yet.
 '''
+from functools import partial
+from mercurial.i18n import _
+from mercurial import cmdutil
+from mercurial import scmutil
+from mercurial import context
+from mercurial import revset
+from mercurial import templatekw
+from mercurial import util
+from mercurial import node
+from mercurial.node import nullid
+from mercurial import discovery
+from mercurial import extensions
 
 
-STATES = (0, )
+_NOPULLPUSH=2
+
+STATES = (0, _NOPULLPUSH)
 def statename(state):
     return str(STATES)
 
-import mercurial.context
-import mercurial.templatekw
 
+# util function
+#############################
+def noderange(repo, revsets):
+    return map(repo.changelog.node,
+               scmutil.revrange(repo, revsets))
 
 # Patch changectx
 #############################
 
 def state(ctx):
     return ctx._repo.nodestate(ctx.node())
-mercurial.context.changectx.state = state
+context.changectx.state = state
 
 # improve template
 #############################
@@ -40,16 +57,126 @@
 def showstate(ctx, **args):
     return ctx.state()
 
+# New revset predicate
+#############################
 
-mercurial.templatekw.keywords['state'] = showstate
+def revsetpublicheads(repo, subset, x):
+    args = revset.getargs(x, 0, 0, 'publicheads takes no arguments')
+    heads = map(repo.changelog.rev, repo._statesheads[0])
+    heads.sort()
+    return heads
+
+def extsetup(ui):
+    revset.symbols['publicheads'] = revsetpublicheads
+
+REVSETHEADS = {0: 'publicheads()'}
+
+# New commands
+#############################
+
+def cmdsetstate(ui, repo, state, *changesets):
+    """turn private changesets into public ones"""
+    #assert repo.ui.configbool('private', 'enable', False)
+    state = int(state) #for now
+    revs = scmutil.revrange(repo, changesets)
+    repo.setstate(state, [repo.changelog.node(rev) for rev in revs])
+    return 0
+
+cmdtable = {
+    'setstate':  (cmdsetstate,   [], _('state <revset>')),
+    }
+
+
+templatekw.keywords['state'] = showstate
+
+
+
+
+
+
+def uisetup(ui):
+    def filter_private(orig, repo, *args,**kwargs):
+        common, heads = orig(repo, *args, **kwargs)
+        return common, repo._reducehead(heads, repo._publicheads)
+    extensions.wrapfunction(discovery, 'findcommonoutgoing', filter_private)
 
 def reposetup(ui, repo):
 
     if not repo.local():
         return
+
     class statefulrepo(repo.__class__):
 
         def nodestate(self, node):
+            rev = self.changelog.rev(node)
+            for head in self._publicheads:
+                revhead = self.changelog.rev(head)
+                if self.changelog.descendant(revhead, rev):
+                    return STATES[1]
             return STATES[0]
 
+        @property
+        def _publicheads(self):
+            if self.ui.configbool('states', 'private', False):
+                return self._statesheads[0]
+            return self.heads()
+
+        @util.propertycache
+        def _statesheads(self):
+            return self._readstatesheads()
+
+
+        def _readstatesheads(self):
+            statesheads = {}
+            try:
+                f = self.opener('publicheads')
+                try:
+                    heads = sorted([node.bin(n) for n in f.read().split() if n])
+                finally:
+                    f.close()
+            except IOError:
+                heads = [nullid]
+            statesheads[0] = heads
+            return statesheads
+
+        def _writestateshead(self):
+            # transaction!
+            f = self.opener('publicheads', 'w', atomictemp=True)
+            try:
+                for h in self._statesheads[0]:
+                    f.write(node.hex(h) + '\n')
+                f.rename()
+            finally:
+                f.close()
+
+        def setstate(self, state, nodes):
+            """freeze targets changeset and it's ancestors.
+
+            Simplify the list of head."""
+            heads = self._statesheads[state]
+            olds = heads[:]
+            heads.extend(nodes)
+            heads[:] = set(heads)
+            heads.sort()
+            if olds != heads:
+                heads[:] = noderange(repo, ["heads(::%s)" % REVSETHEADS[state]])
+                heads.sort()
+            if olds != heads:
+                self._writestateshead()
+
+        def _reducehead(self, candidates, max):
+            selected = set()
+            for candidate in candidates:
+                rev = self.changelog.rev(candidate)
+                ok = True
+                for h in max:
+                    revh = self.changelog.rev(h)
+                    if self.changelog.descendant(revh, rev):
+                        ok = False
+                        selected.add(h)
+                if ok:
+                    selected.add(candidate)
+            return sorted(selected)
+
     repo.__class__ = statefulrepo
+
--- a/tests/test-state.t	Fri May 20 16:16:34 2011 +0200
+++ b/tests/test-state.t	Fri May 20 19:14:19 2011 +0200
@@ -3,11 +3,73 @@
   > EOF
   $ echo "states=$(echo $(dirname $TESTDIR))/states.py" >> $HGRCPATH
 
-  $ hg init repo
-  $ cd repo
+  $ hg init local
+  $ hg init remote1
+  $ hg init remote2
+  $ cd local
   $ echo "celestine" > babar
   $ hg add babar
   $ hg ci -m "add babar"
-  $ hg log --template='{node}: {state}\n'
-  5caa672bac265926428463f2bee6e8903972ce31: 0
+  $ echo "la veille dame" > babar
+  $ hg ci -m "add dame"
+  $ hg log --template='{rev}:{node|short}: {state}\n'
+  1:710fe444b3b0: 0
+  0:5caa672bac26: 0
+  $ hg out  ../remote1 --template='{rev}:{node|short}\n'
+  comparing with ../remote1
+  searching for changes
+  0:5caa672bac26
+  1:710fe444b3b0
+  $ hg push ../remote1
+  pushing to ../remote1
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  $ hg setstate 0 1 # until we fix push
+  $ echo "cornelius" >> babar
+  $ hg ci -m "great old one"
+  $ echo "flore" >> babar
+  $ hg ci -m "children"
+  $ hg log --template='{rev}:{node|short}: {state}\n'
+  3:3f5e297fd1c6: 0
+  2:dc0a5281e2d9: 0
+  1:710fe444b3b0: 0
+  0:5caa672bac26: 0
 
+  $ cat >> $HGRCPATH <<EOF
+  > [states]
+  > private=yes
+  > EOF
+  $ hg log --template='{rev}:{node|short}: {state}\n'
+  3:3f5e297fd1c6: 2
+  2:dc0a5281e2d9: 2
+  1:710fe444b3b0: 0
+  0:5caa672bac26: 0
+  $ hg out  ../remote1 --template='{rev}:{node|short}\n'
+  comparing with ../remote1
+  searching for changes
+  no changes found
+  [1]
+  $ hg push  ../remote1
+  pushing to ../remote1
+  searching for changes
+  no changes found
+
+  $ hg out  ../remote2 --template='{rev}:{node|short}\n'
+  comparing with ../remote2
+  searching for changes
+  0:5caa672bac26
+  1:710fe444b3b0
+  $ hg push  ../remote2
+  pushing to ../remote2
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+
+
+
+