strip: support multiple revisions
authorNicolas Dumazet <nicdumz.commits@gmail.com>
Sat, 17 Jul 2010 00:47:06 +0900
changeset 11789 e2bce1c717fa
parent 11788 b3de1438028d
child 11790 ba9957bcfb7c
strip: support multiple revisions
hgext/mq.py
tests/test-mq-strip
tests/test-mq-strip.out
tests/test-mq.out
--- a/hgext/mq.py	Thu Aug 12 16:35:34 2010 +0900
+++ b/hgext/mq.py	Sat Jul 17 00:47:06 2010 +0900
@@ -513,7 +513,7 @@
 
         # apply failed, strip away that rev and merge.
         hg.clean(repo, head)
-        self.strip(repo, n, update=False, backup='strip')
+        self.strip(repo, [n], update=False, backup='strip')
 
         ctx = repo[rev]
         ret = hg.merge(repo, rev)
@@ -895,7 +895,7 @@
         finally:
             release(wlock)
 
-    def strip(self, repo, rev, update=True, backup="all", force=None):
+    def strip(self, repo, revs, update=True, backup="all", force=None):
         wlock = lock = None
         try:
             wlock = repo.wlock()
@@ -903,12 +903,13 @@
 
             if update:
                 self.check_localchanges(repo, force=force, refresh=False)
-                urev = self.qparents(repo, rev)
+                urev = self.qparents(repo, revs[0])
                 hg.clean(repo, urev)
                 repo.dirstate.write()
 
             self.removeundo(repo)
-            repair.strip(self.ui, repo, rev, backup)
+            for rev in revs:
+                repair.strip(self.ui, repo, rev, backup)
             # strip may have unbundled a set of backed up revisions after
             # the actual strip
             self.removeundo(repo)
@@ -1197,7 +1198,7 @@
             for patch in reversed(self.applied[start:end]):
                 self.ui.status(_("popping %s\n") % patch.name)
             del self.applied[start:end]
-            self.strip(repo, rev, update=False, backup='strip')
+            self.strip(repo, [rev], update=False, backup='strip')
             if self.applied:
                 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
             else:
@@ -1377,7 +1378,7 @@
                 repo.dirstate.setparents(*cparents)
                 self.applied.pop()
                 self.applied_dirty = 1
-                self.strip(repo, top, update=False,
+                self.strip(repo, [top], update=False,
                            backup='strip')
             except:
                 repo.dirstate.invalidate()
@@ -1532,7 +1533,7 @@
                     update = True
                 else:
                     update = False
-                self.strip(repo, rev, update=update, backup='strip')
+                self.strip(repo, [rev], update=update, backup='strip')
         if qpp:
             self.ui.warn(_("saved queue repository parents: %s %s\n") %
                          (short(qpp[0]), short(qpp[1])))
@@ -1934,7 +1935,7 @@
         if qbase:
             ui.note(_('stripping applied patches from destination '
                       'repository\n'))
-            dr.mq.strip(dr, qbase, update=False, backup=None)
+            dr.mq.strip(dr, [qbase], update=False, backup=None)
         if not opts['noupdate']:
             ui.note(_('updating destination repository\n'))
             hg.update(dr, dr.changelog.tip())
@@ -2396,14 +2397,12 @@
             pass
     return 0
 
-def strip(ui, repo, rev, **opts):
-    """strip a changeset and all its descendants from the repository
-
-    The strip command removes all changesets whose local revision
-    number is greater than or equal to REV, and then restores any
-    changesets that are not descendants of REV. If the working
-    directory has uncommitted changes, the operation is aborted unless
-    the --force flag is supplied.
+def strip(ui, repo, *revs, **opts):
+    """strip changesets and all their descendants from the repository
+
+    The strip command removes the specified changesets and all their
+    descendants. If the working directory has uncommitted changes,
+    the operation is aborted unless the --force flag is supplied.
 
     If a parent of the working directory is stripped, then the working
     directory will automatically be updated to the most recent
@@ -2426,30 +2425,42 @@
     elif opts['nobackup']:
         backup = 'none'
 
-    rev = repo.lookup(rev)
-    p = repo.dirstate.parents()
     cl = repo.changelog
-    update = True
-    if p[0] == nullid:
-        update = False
-    elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
-        update = False
-    elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
-        update = False
+    revs = set(cl.rev(repo.lookup(r)) for r in revs)
+
+    descendants = set(cl.descendants(*revs))
+    strippedrevs = revs.union(descendants)
+    roots = revs.difference(descendants)
+
+    update = False
+    # if one of the wdir parent is stripped we'll need
+    # to update away to an earlier revision
+    for p in repo.dirstate.parents():
+        if p != nullid and cl.rev(p) in strippedrevs:
+            update = True
+            break
+
+    rootnodes = set(cl.node(r) for r in roots)
 
     q = repo.mq
     if q.applied:
-        if rev == cl.ancestor(repo.lookup('qtip'), rev):
+        # refresh queue state if we're about to strip
+        # applied patches
+        if cl.rev(repo.lookup('qtip')) in strippedrevs:
             q.applied_dirty = True
             start = 0
             end = len(q.applied)
-            applied_list = [i.node for i in q.applied]
-            if rev in applied_list:
-                start = applied_list.index(rev)
+            for i, statusentry in enumerate(q.applied):
+                if statusentry.node in rootnodes:
+                    # if one of the stripped roots is an applied
+                    # patch, only part of the queue is stripped
+                    start = i
+                    break
             del q.applied[start:end]
             q.save_dirty()
 
-    repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
+    repo.mq.strip(repo, list(rootnodes), backup=backup, update=update,
+                  force=opts['force'])
     return 0
 
 def select(ui, repo, *args, **opts):
@@ -3008,7 +3019,7 @@
                                   ' number greater than REV which are not'
                                   ' descendants of REV (DEPRECATED)')),
            ('n', 'nobackup', None, _('no backups'))],
-          _('hg strip [-f] [-n] REV')),
+          _('hg strip [-f] [-n] REV...')),
      "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
     "qunapplied":
         (unapplied,
--- a/tests/test-mq-strip	Thu Aug 12 16:35:34 2010 +0900
+++ b/tests/test-mq-strip	Sat Jul 17 00:47:06 2010 +0900
@@ -4,16 +4,20 @@
 
 echo "[extensions]" >> $HGRCPATH
 echo "mq=" >> $HGRCPATH
+echo "graphlog=" >> $HGRCPATH
 
+restore() {
+    hg unbundle -q .hg/strip-backup/*
+    rm .hg/strip-backup/*
+}
 teststrip() {
     hg up -C $1
     echo % before update $1, strip $2
     hg parents
-    hg strip $2 | hidebackup
+    hg --traceback strip $2 | hidebackup
     echo % after update $1, strip $2
     hg parents
-    hg unbundle -q .hg/strip-backup/*
-    rm .hg/strip-backup/*
+    restore
 }
 
 hg init test
@@ -53,6 +57,25 @@
 hg strip 4 2>&1 | hidebackup
 echo % after strip of merge parent
 hg parents
+restore
+
+hg up
+hg glog
+echo % 2 is parent of 3, only one strip should happen
+hg strip 2 3 | hidebackup
+hg glog
+restore
+hg glog
+echo % 2 different branches: 2 strips
+hg strip 2 4 | hidebackup
+hg glog
+restore
+echo % 2 different branches and a common ancestor: 1 strip
+hg strip 1 2 4 | hidebackup
+restore
+
+# remove branchy history for qimport tests
+hg strip 3 | hidebackup
 
 #strip of applied mq should cleanup status file
 hg up -C 3
--- a/tests/test-mq-strip.out	Thu Aug 12 16:35:34 2010 +0900
+++ b/tests/test-mq-strip.out	Sat Jul 17 00:47:06 2010 +0900
@@ -166,6 +166,103 @@
 summary:     b
 
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+@  changeset:   4:264128213d29
+|  tag:         tip
+|  parent:      1:ef3a871183d7
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+| o  changeset:   3:443431ffac4f
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:00 1970 +0000
+| |  summary:     e
+| |
+| o  changeset:   2:65bd5f99a4a3
+|/   user:        test
+|    date:        Thu Jan 01 00:00:00 1970 +0000
+|    summary:     d
+|
+o  changeset:   1:ef3a871183d7
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:9ab35a2d17cb
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% 2 is parent of 3, only one strip should happen
+saved backup bundle to 
+@  changeset:   2:264128213d29
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+o  changeset:   1:ef3a871183d7
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:9ab35a2d17cb
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+o  changeset:   4:443431ffac4f
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   3:65bd5f99a4a3
+|  parent:      1:ef3a871183d7
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+| @  changeset:   2:264128213d29
+|/   user:        test
+|    date:        Thu Jan 01 00:00:00 1970 +0000
+|    summary:     c
+|
+o  changeset:   1:ef3a871183d7
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:9ab35a2d17cb
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% 2 different branches: 2 strips
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+saved backup bundle to 
+saved backup bundle to 
+@  changeset:   2:65bd5f99a4a3
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   1:ef3a871183d7
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:9ab35a2d17cb
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% 2 different branches and a common ancestor: 1 strip
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+saved backup bundle to 
+saved backup bundle to 
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % applied patches before strip
 2.diff
 3.diff
--- a/tests/test-mq.out	Thu Aug 12 16:35:34 2010 +0900
+++ b/tests/test-mq.out	Sat Jul 17 00:47:06 2010 +0900
@@ -59,7 +59,7 @@
  qseries      print the entire series file
  qtop         print the name of the current patch
  qunapplied   print the patches not yet applied
- strip        strip a changeset and all its descendants from the repository
+ strip        strip changesets and all their descendants from the repository
 
 use "hg -v help mq" to show aliases and global options
 adding a