[states] add --exact option to `hg <state> <nodes>` that allows to backward states bondaries
authorAlain Leufroy <alain.leufroy@logilab.fr>
Sun, 25 Sep 2011 12:43:00 +0200
changeset 96 d5170cc7881c
parent 95 5dcece86aeb0
child 97 e672cb1263cb
[states] add --exact option to `hg <state> <nodes>` that allows to backward states bondaries
hgext/states.py
tests/test-states-exact.t
--- a/hgext/states.py	Wed Sep 21 03:52:13 2011 +0200
+++ b/hgext/states.py	Sun Sep 25 12:43:00 2011 +0200
@@ -197,8 +197,6 @@
 
 TODO:
 
-- implement --exact
-
 - implement consistency check
 
 - implement --force
@@ -533,20 +531,30 @@
 
 # automatic generation of command that set state
 def makecmd(state):
-    def cmdmoveheads(ui, repo, *changesets):
+    def cmdmoveheads(ui, repo, *changesets, **opts):
         """set revisions in %s state
 
         This command also alter state of ancestors if necessary.
         """ % state
+        if not state in repo._enabledstates:
+            raise error.Abort(
+                    _('state %s is not activated' % state),
+                    hint=_('try ``hg states %s`` before' % state))
+        if opts.get('exact'):
+            repo.setstate_unsafe(state, changesets)
+            return 0
         revs = scmutil.revrange(repo, changesets)
         repo.setstate(state, [repo.changelog.node(rev) for rev in revs])
         return 0
     return cmdmoveheads
 
 for state in STATES:
-    if state.trackheads:
-        cmdmoveheads = makecmd(state)
-        cmdtable[state.name] = (cmdmoveheads, [], '<revset>')
+    cmdmoveheads = makecmd(state)
+    cmdtable[state.name] = (cmdmoveheads, [
+        ('e', 'exact', False, _('move boundary so that revs are exactly in '
+                               'state <state> ( all([rev.state == <state> for '
+                                'rev in revs]))'))
+        ], '<revset>')
 
 # Pushkey mechanism for mutable
 #########################################
@@ -795,6 +803,32 @@
             """{ state-object -> set(defining head)} mapping"""
             return _readstatesheads(self)
 
+        def setstate_unsafe(self, state, changesets):
+            """Change state of targets changesets and it's ancestors.
+
+            Simplify the list of heads.
+
+            Unlike ``setstate``, the "lower" states are also changed
+            """
+            #modify "lower" states
+            req_nodes_rst = '|'.join('((%s)::)' % rst for rst in changesets)
+            for st in STATES:
+                if st >= state: # only modify lower state heads for now
+                    continue
+                try:
+                    heads = self._statesheads[st]
+                except KeyError: # forget non-activated states
+                    continue
+                olds = heads[:]
+                rst = "heads((::%s()) - (%s))" % (st.headssymbol, req_nodes_rst)
+                heads[:] = noderange(repo, [rst])
+                if olds != heads:
+                    _writestateshead(self)
+            #modify the state
+            if state in self._statesheads:
+                revs = scmutil.revrange(repo, changesets)
+                repo.setstate(state, [repo.changelog.node(rev) for rev in revs])
+
         def setstate(self, state, nodes):
             """change state of targets changeset and it's ancestors.
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-states-exact.t	Sun Sep 25 12:43:00 2011 +0200
@@ -0,0 +1,282 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [web]
+  > push_ssl = false
+  > allow_push = *
+  > [extensions]
+  > EOF
+  $ echo "states=$(echo $(dirname $TESTDIR))/hgext/states.py" >> $HGRCPATH
+
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "$1"
+  > }
+  $ alias hglog='hg log --template "{desc|short} {rev} {state}\n"'
+
+  $ hg init alpha
+  $ cd alpha
+  $ hg states ready draft
+  $ mkcommit a
+  $ mkcommit b
+  $ mkcommit c
+  $ mkcommit d
+  $ mkcommit e
+  $ mkcommit f
+
+|0 - 1 - 2 - 3 - 4 - 5
+
+  $ hg ready 3
+  $ hg published 1
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 published
+  a 0 published
+
+very simple case
+  $ hg ready --exact 1
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 published
+
+required state not in statesheads
+  $ hg draft --exact 3
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 draft
+  c 2 ready
+  b 1 ready
+  a 0 published
+
+remove all other states
+  $ hg draft --exact 0
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 draft
+  c 2 draft
+  b 1 draft
+  a 0 draft
+
+draft was not there before
+  $ hg ready 5
+  $ hg draft --exact 4
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 ready
+
+already in the required state
+  $ hg draft --exact 5
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 ready
+
+backward and foreward
+  $ hg published 1
+  $ hg ready --exact 1:4
+  $ hglog
+  f 5 draft
+  e 4 ready
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 published
+
+with complex revset
+  $ hg draft --exact 'readyheads()'
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 published
+  $ hg ready --exact 'publishedheads()'
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 ready
+
+Work with branches now
+  $ hg up 1
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ mkcommit g
+  created new head
+  $ mkcommit h
+  $ hg up 3
+  2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ mkcommit i
+  created new head
+  $ mkcommit j
+
+|              / - - - - 6 - 7
+|0 - 1 - 2 - 3 - 4 - 5
+|      \ - - - - - - - - - - - - 8 - 9
+
+Set the new branches as ready
+  $ hg ready 9 7
+  $ hglog
+  j 9 ready
+  i 8 ready
+  h 7 ready
+  g 6 ready
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 ready
+
+with composite revset
+  $ hg draft --exact 5:5 7:8
+  $ hglog
+  j 9 draft
+  i 8 draft
+  h 7 draft
+  g 6 ready
+  f 5 draft
+  e 4 draft
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 ready
+
+cross braches
+  $ hg published 5 7 9
+  $ hg draft --exact 4:6
+  $ hglog
+  j 9 published
+  i 8 published
+  h 7 draft
+  g 6 draft
+  f 5 draft
+  e 4 draft
+  d 3 published
+  c 2 published
+  b 1 published
+  a 0 published
+
+Ok more complicated stuffs
+  $ hg up 7
+  2 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ mkcommit k
+  $ hg published 5 9 10
+
+|              / - - - - 6 - 7 - - - - - 10
+|0 - 1 - 2 - 3 - 4 - 5
+|      \ - - - - - - - - - - - - 8 - 9
+
+cross branches and propagation on the same branche
+  $ hg draft --exact 5:8
+  $ hglog
+  k 10 draft
+  j 9 draft
+  i 8 draft
+  h 7 draft
+  g 6 draft
+  f 5 draft
+  e 4 published
+  d 3 published
+  c 2 published
+  b 1 published
+  a 0 published
+
+
+cross branches and propagation on multiple branches
+  $ hg published 10
+  $ hg ready --exact 3 6:8
+  $ hglog
+  k 10 ready
+  j 9 draft
+  i 8 ready
+  h 7 ready
+  g 6 ready
+  f 5 draft
+  e 4 ready
+  d 3 ready
+  c 2 published
+  b 1 published
+  a 0 published
+
+propagate ready on multiple branches taht contains draft states
+  $ hg ready --exact 1
+  $ hglog
+  k 10 ready
+  j 9 draft
+  i 8 ready
+  h 7 ready
+  g 6 ready
+  f 5 draft
+  e 4 ready
+  d 3 ready
+  c 2 ready
+  b 1 ready
+  a 0 published
+
+brute propagation
+  $ hg draft --exact 0
+  $ hglog
+  k 10 draft
+  j 9 draft
+  i 8 draft
+  h 7 draft
+  g 6 draft
+  f 5 draft
+  e 4 draft
+  d 3 draft
+  c 2 draft
+  b 1 draft
+  a 0 draft
+
+forget non activated state
+  $ hg init beta
+  $ cd beta
+  $ hg states draft
+  $ mkcommit a
+  $ mkcommit b
+  $ mkcommit c
+  $ mkcommit d
+  $ mkcommit e
+  $ mkcommit f
+  $ hg published 5
+  $ hg draft --exact 3
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 draft
+  c 2 published
+  b 1 published
+  a 0 published
+  $ hg states
+  published
+  draft
+  $ hg ready 3
+  abort: state ready is not activated
+  (try ``hg states ready`` before)
+  [255]
+  $ hglog
+  f 5 draft
+  e 4 draft
+  d 3 draft
+  c 2 published
+  b 1 published
+  a 0 published
+