Commits

Pierre-Yves David committed beabde9

Add "private" concept (2). private changeset are hiden suppose to be hidden for
pull and push. For now it works for out and push

Comments (0)

Files changed (2)

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

tests/test-state.t

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