Add "private" concept (2). private changeset are hiden suppose to be hidden for
pull and push. For now it works for out and push
--- 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
+
+
+
+