qsync: support for synchronisation with applied patches from outer space and more
authorDavid Douard <david.douard@logilab.fr>
Fri, 04 May 2012 14:33:35 +0200
changeset 220 ff3158d0d7e8
parent 219 cfdab01ca8a0
child 221 d43b72724b84
qsync: support for synchronisation with applied patches from outer space and more :more: - also add some tests - kill OLDBRANCHNAME - several minor cleaning
hgext/qsync.py
tests/test-qsync.t
--- a/hgext/qsync.py	Wed May 02 14:08:21 2012 +0200
+++ b/hgext/qsync.py	Fri May 04 14:33:35 2012 +0200
@@ -1,7 +1,7 @@
 
 import re
-
 from cStringIO import StringIO
+import json
 
 from mercurial.i18n import _
 from mercurial import commands
@@ -13,18 +13,12 @@
 from mercurial import scmutil
 from mercurial import error
 from mercurial import extensions
-
-
-import re
-
-import json
-
+from mercurial import phases
 
 ### old compat code
 #############################
 
 BRANCHNAME="qsubmit2"
-OLDBRANCHNAME="pyves-qsubmit"
 
 ### new command
 #############################
@@ -59,7 +53,6 @@
                  interleaved changeset will appear interleaved.
     '''
 
-    review = None
     review = 'edit'
     if opts['review_all']:
         review = 'all'
@@ -67,10 +60,7 @@
     try:
         parent = mqrepo[BRANCHNAME]
     except error.RepoLookupError:
-        try:
-            parent =  mqrepo[OLDBRANCHNAME]
-        except error.RepoLookupError:
-            parent = initqsubmit(mqrepo)
+        parent = initqsubmit(mqrepo)
     store, data, touched = fillstore(repo, parent)
     if not touched:
         raise util.Abort('Nothing changed')
@@ -78,15 +68,45 @@
     # mark some as ready for review
     message = 'qsubmit commit\n\n'
     review_list = []
+    applied_list = []
     if review:
+        olddata = get_old_data(parent)
+        oldfiles = dict([(name, ctxhex) for ctxhex, name in olddata])
+
         for patch_name in touched:
             try:
                 store.getfile(patch_name)
                 review_list.append(patch_name)
             except IOError:
-                pass
+                oldnode = oldfiles[patch_name]
+                obsolete = extensions.find('obsolete')
+                newnodes = obsolete.newerversion(repo, oldnode)
+                if newnodes:
+                    newnodes = [n for n in newnodes if n] # remove killing
+                if not newnodes:
+                    # changeset has been killed (eg. reject)
+                    pass
+                else:
+                    assert len(newnodes) == 1 # conflict!!!
+                    newnode = newnodes[0]
+                    assert len(newnode) == 1 # split unsupported for now
+                    newnode = list(newnode)[0]
+                    # XXX unmanaged case where a cs is obsoleted by an unavailable one
+                    #if newnode.node() not in repo.changelog.nodemap:
+                    #    raise util.Abort('%s is obsoleted by an unknown node %s'% (oldnode, newnode))
+                    ctx = repo[newnode]
+                    if ctx.phase() == phases.public:
+                        # applied
+                        applied_list.append(patch_name)
+                    elif ctx.phase() == phases.secret:
+                        # already exported changeset is now secret
+                        repo.ui.warn("An already exported changeset is now secret!!!")
+                    else:
+                        # draft
+                        assert False, "Should be exported"
 
     if review:
+        message += '\n'.join('* applied %s' % x for x in applied_list)
         message += '\n'.join('* %s ready for review' % x for x in review_list)
     memctx = patch.makememctx(mqrepo, (parent.node(), nullid),
                               message,
@@ -122,7 +142,7 @@
         return []
 
 def get_current_data(repo):
-    """Return what would be exported if not previous data exists"""
+    """Return what would be exported if no previous data exists"""
     data = []
     for ctx in repo.set('draft() - (obsolete() + merge())'):
         name = makename(ctx)
@@ -188,6 +208,7 @@
     finaldata.sort(key=lambda x: sort_key(repo[x[0]]))
     # sort touched too (ease review list)
     stouched = [f[1] for f in finaldata if f[1] in touched]
+    stouched += [x for x in touched if x not in stouched]
     return finaldata, stouched
 
 def sort_key(ctx):
@@ -196,7 +217,7 @@
 
 
 def fillstore(repo, basemqctx):
-    """file store with patch data"""
+    """fill store with patch data"""
     olddata = get_old_data(basemqctx)
     newdata = get_current_data(repo)
     store = patch.filestore()
@@ -207,7 +228,6 @@
         store.setfile('series', series, (False, False))
 
         # export data to ease futur work
-        series ='\n'.join(d[1] for d in data) + '\n'
         store.setfile('qsubmitdata', json.dumps(data, indent=True),
                       (False, False))
     finally:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-qsync.t	Fri May 04 14:33:35 2012 +0200
@@ -0,0 +1,212 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [defaults]
+  > amend=-d "0 0"
+  > [web]
+  > push_ssl = false
+  > allow_push = *
+  > [phases]
+  > publish = False
+  > [alias]
+  > qlog = log --template='{rev} - {node|short} {desc} ({phase})\n'
+  > mqlog = log --mq --template='{rev} - {desc}\n'
+  > [diff]
+  > git = 1
+  > unified = 0
+  > [extensions]
+  > hgext.rebase=
+  > hgext.graphlog=
+  > hgext.mq=
+  > EOF
+  $ echo "obsolete=$(echo $(dirname $TESTDIR))/hgext/obsolete.py" >> $HGRCPATH
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+  $ echo "qsync=$(echo $(dirname $TESTDIR))/hgext/qsync.py" >> $HGRCPATH
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+
+basic sync
+
+  $ hg init local
+  $ cd local
+  $ hg qinit -c
+  $ hg qci -m "initial commit"
+  $ mkcommit a
+  $ mkcommit b
+  $ hg qlog
+  1 - 7c3bad9141dc add b (draft)
+  0 - 1f0dee641bb7 add a (draft)
+  $ hg qsync -a
+  $ hg mqlog
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+basic sync II
+
+  $ hg init local
+  $ cd local
+  $ hg qinit -c
+  $ hg qci -m "initial commit"
+  $ mkcommit a
+  $ mkcommit b
+  $ hg qlog
+  1 - 7c3bad9141dc add b (draft)
+  0 - 1f0dee641bb7 add a (draft)
+  $ hg qsync -a
+  $ hg mqlog
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+  $ echo "b" >> b
+  $ hg amend
+  $ hg qsync -a
+  $ hg mqlog
+  3 - qsubmit commit
+  
+  * DEFAULT-add_b.diff ready for review
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+  $ hg up -r 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "a" >> a
+  $ hg amend
+  1 new unstables changesets
+  $ hg graft -O 3
+  grafting revision 3
+  $ hg qsync -a
+  $ hg mqlog
+  4 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  3 - qsubmit commit
+  
+  * DEFAULT-add_b.diff ready for review
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+sync with published changeset
+
+  $ hg init local
+  $ cd local
+  $ hg qinit -c
+  $ hg qci -m "initial commit"
+  $ mkcommit a
+  $ mkcommit b
+  $ hg qlog
+  1 - 7c3bad9141dc add b (draft)
+  0 - 1f0dee641bb7 add a (draft)
+  $ hg qsync -a
+  $ hg mqlog
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+  $ hg phase -p 0
+  $ hg qsync -a
+  $ hg mqlog
+  3 - qsubmit commit
+  
+  * applied DEFAULT-add_a.diff
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+  $ mkcommit c
+  $ mkcommit d
+  $ hg qsync -a
+  $ hg mqlog
+  4 - qsubmit commit
+  
+  * DEFAULT-add_c.diff ready for review
+  * DEFAULT-add_d.diff ready for review
+  3 - qsubmit commit
+  
+  * applied DEFAULT-add_a.diff
+  2 - qsubmit commit
+  
+  * DEFAULT-add_a.diff ready for review
+  * DEFAULT-add_b.diff ready for review
+  1 - qsubmit init
+  0 - initial commit
+
+  $ cd ..
+  $ hg qclone -U local local2
+  $ cd local2
+  $ hg qlog
+  3 - 47d2a3944de8 add d (draft)
+  2 - 4538525df7e2 add c (draft)
+  1 - 7c3bad9141dc add b (draft)
+  0 - 1f0dee641bb7 add a (public)
+  $ hg strip -n 1 --no-backup
+  $ hg up
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg up --mq 4
+  6 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg qseries
+  DEFAULT-add_b.diff
+  DEFAULT-add_c.diff
+  DEFAULT-add_d.diff
+  $ hg qpush
+  applying DEFAULT-add_b.diff
+  now at: DEFAULT-add_b.diff
+  $ hg qfinish -a
+  $ hg phase -p .
+  $ hg qci -m "applied DEFAULT-add_b.diff"
+  $ cd ../local
+  $ hg pull ../local2
+  pulling from ../local2
+  searching for changes
+  no changes found
+  $ hg pull --mq ../local2/.hg/patches
+  pulling from ../local2/.hg/patches
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  (run 'hg update' to get a working copy)
+  $ hg qlog
+  3 - 47d2a3944de8 add d (draft)
+  2 - 4538525df7e2 add c (draft)
+  1 - 7c3bad9141dc add b (public)
+  0 - 1f0dee641bb7 add a (public)
+  $ hg mqlog -l 1
+  5 - applied DEFAULT-add_b.diff
+  $ hg status --mq --rev tip:-2
+  M series
+  A DEFAULT-add_b.diff
+  $ hg qsync -a
+  $ hg status --mq --rev tip:-2
+  M qsubmitdata
+  $ hg mqlog -l 1
+  6 - qsubmit commit
+  
+  * applied DEFAULT-add_b.diff
+  $ hg qsync -a
+  abort: Nothing changed
+  [255]