Patchwork [1,of,2,RESENT] strip: introduce a soft strip option

login
register
mail settings
Submitter Boris Feld
Date Feb. 13, 2019, 2:38 p.m.
Message ID <61ec4a834e2c88056ff4.1550068711@Laptop-Boris.lan>
Download mbox | patch
Permalink /patch/38719/
State Accepted
Headers show

Comments

Boris Feld - Feb. 13, 2019, 2:38 p.m.
# HG changeset patch
# User Boris Feld <boris.feld@octobus.net>
# Date 1539697680 -7200
#      Tue Oct 16 15:48:00 2018 +0200
# Node ID 61ec4a834e2c88056ff47c0d3a7ff3bcb0f0d912
# Parent  61415361e90684a8c7a031413e9182f51937c2e7
# EXP-Topic archived-phase-UX
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 61ec4a834e2c
strip: introduce a soft strip option

This is the first user-accessible way to use the archived phase introduced in
4.8. This implements a feature discussed during the Stockholm sprint, using
the archived phase for hiding changesets.

The archived phase behaves exactly as stripping: changesets are no longer
visible, but pulling/unbundling them will make then reappear. The only notable
difference is that unlike hard stripping, soft stripping does not affect
obsmarkers.

The next changeset will make use of the archived phase for history rewriting
command. However, having a way to manually trigger the feature first seems a
necessary step before exposing users to this phase; there is a way to
un-archived changesets (unbundling), so there must be a way to archive them
again.

Adding a flag to strip is a good way to provide access to the feature without
taking a too big risk on the final UI we want. The flag is experimental so it
won't be exposed by default.

Using the archived phase is faster and less traumatic for the repository than
actually stripping changesets.
Gregory Szorc - Feb. 26, 2019, 2:32 a.m.
I have queued this series!

I think there's room to work on the terminology and the user-facing bits.
But this feature is experimental and this all can be worked on later.

I think the most important user-facing issue is differentiating "archived"
changesets in `hg log` output. In the test added in this commit, `hg log -G
--hidden` doesn't at all indicate that a changeset is archived. With
obsolete changesets, we use "x" for the node character. Should we recycle
this symbol for archived changesets or use something else? We should at
least add a label to the changeset so the templating layer can color things
differently, if it doesn't already do so. (I suspect it does since phases
translate to labels.)

I think we'll also want some output when changesets are promoted from
archived back to something visible. The last test in this patch
demonstrates that nothing special is printed and the `hg unbundle`
operation looks like a no-op! We'll probably want to do something similar
to the messaging we now print during `hg pull` regarding phase changes.

This is a great feature and it will vastly improve performance for people
not using evolve. I look forward to seeing it stabilized!

On Wed, Feb 13, 2019 at 6:38 AM Boris Feld <boris.feld@octobus.net> wrote:

> # HG changeset patch
> # User Boris Feld <boris.feld@octobus.net>
> # Date 1539697680 -7200
> #      Tue Oct 16 15:48:00 2018 +0200
> # Node ID 61ec4a834e2c88056ff47c0d3a7ff3bcb0f0d912
> # Parent  61415361e90684a8c7a031413e9182f51937c2e7
> # EXP-Topic archived-phase-UX
> # Available At https://bitbucket.org/octobus/mercurial-devel/
> #              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r
> 61ec4a834e2c
> strip: introduce a soft strip option
>
> This is the first user-accessible way to use the archived phase introduced
> in
> 4.8. This implements a feature discussed during the Stockholm sprint, using
> the archived phase for hiding changesets.
>
> The archived phase behaves exactly as stripping: changesets are no longer
> visible, but pulling/unbundling them will make then reappear. The only
> notable
> difference is that unlike hard stripping, soft stripping does not affect
> obsmarkers.
>
> The next changeset will make use of the archived phase for history
> rewriting
> command. However, having a way to manually trigger the feature first seems
> a
> necessary step before exposing users to this phase; there is a way to
> un-archived changesets (unbundling), so there must be a way to archive them
> again.
>
> Adding a flag to strip is a good way to provide access to the feature
> without
> taking a too big risk on the final UI we want. The flag is experimental so
> it
> won't be exposed by default.
>
> Using the archived phase is faster and less traumatic for the repository
> than
> actually stripping changesets.
>
> diff --git a/hgext/strip.py b/hgext/strip.py
> --- a/hgext/strip.py
> +++ b/hgext/strip.py
> @@ -76,7 +76,8 @@ def _findupdatetarget(repo, nodes):
>
>      return unode
>
> -def strip(ui, repo, revs, update=True, backup=True, force=None,
> bookmarks=None):
> +def strip(ui, repo, revs, update=True, backup=True, force=None,
> bookmarks=None,
> +          soft=False):
>      with repo.wlock(), repo.lock():
>
>          if update:
> @@ -85,7 +86,10 @@ def strip(ui, repo, revs, update=True, b
>              hg.clean(repo, urev)
>              repo.dirstate.write(repo.currenttransaction())
>
> -        repair.strip(ui, repo, revs, backup)
> +        if soft:
> +            repair.softstrip(ui, repo, revs, backup)
> +        else:
> +            repair.strip(ui, repo, revs, backup)
>
>          repomarks = repo._bookmarks
>          if bookmarks:
> @@ -110,7 +114,10 @@ def strip(ui, repo, revs, update=True, b
>            ('k', 'keep', None, _("do not modify working directory during "
>                                  "strip")),
>            ('B', 'bookmark', [], _("remove revs only reachable from given"
> -                                  " bookmark"), _('BOOKMARK'))],
> +                                  " bookmark"), _('BOOKMARK')),
> +          ('', 'soft', None,
> +          _("simply drop changesets from visible history
> (EXPERIMENTAL)")),
> +         ],
>            _('hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
>            helpcategory=command.CATEGORY_MAINTENANCE)
>  def stripcmd(ui, repo, *revs, **opts):
> @@ -235,6 +242,7 @@ def stripcmd(ui, repo, *revs, **opts):
>
>
>          strip(ui, repo, revs, backup=backup, update=update,
> -              force=opts.get('force'), bookmarks=bookmarks)
> +              force=opts.get('force'), bookmarks=bookmarks,
> +              soft=opts['soft'])
>
>      return 0
> diff --git a/mercurial/repair.py b/mercurial/repair.py
> --- a/mercurial/repair.py
> +++ b/mercurial/repair.py
> @@ -252,6 +252,24 @@ def strip(ui, repo, nodelist, backup=Tru
>      # extensions can use it
>      return backupfile
>
> +def softstrip(ui, repo, nodelist, backup=True, topic='backup'):
> +    """perform a "soft" strip using the archived phase"""
> +    tostrip = [c.node() for c in repo.set('sort(%ln::)', nodelist)]
> +    if not tostrip:
> +        return None
> +
> +    newbmtarget, updatebm = _bookmarkmovements(repo, tostrip)
> +    if backup:
> +        node = tostrip[0]
> +        backupfile = _createstripbackup(repo, tostrip, node, topic)
> +
> +    with repo.transaction('strip') as tr:
> +        phases.retractboundary(repo, tr, phases.archived, tostrip)
> +        bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm]
> +        repo._bookmarks.applychanges(repo, tr, bmchanges)
> +    return backupfile
> +
> +
>  def _bookmarkmovements(repo, tostrip):
>      # compute necessary bookmark movement
>      bm = repo._bookmarks
> diff --git a/tests/test-phase-archived.t b/tests/test-phase-archived.t
> new file mode 100644
> --- /dev/null
> +++ b/tests/test-phase-archived.t
> @@ -0,0 +1,77 @@
> +=========================================================
> +Test features and behaviors related to the archived phase
> +=========================================================
> +
> +  $ cat << EOF >> $HGRCPATH
> +  > [format]
> +  > internal-phase=yes
> +  > [extensions]
> +  > strip=
> +  > [experimental]
> +  > EOF
> +
> +  $ hg init repo
> +  $ cd repo
> +  $ echo  root > a
> +  $ hg add a
> +  $ hg ci -m 'root'
> +
> +Test that bundle can unarchive a changeset
> +------------------------------------------
> +
> +  $ echo foo >> a
> +  $ hg st
> +  M a
> +  $ hg ci -m 'unbundletesting'
> +  $ hg log -G
> +  @  changeset:   1:883aadbbf309
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     unbundletesting
> +  |
> +  o  changeset:   0:c1863a3840c6
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     root
> +
> +  $ hg strip --soft --rev '.'
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  saved backup bundle to
> $TESTTMP/repo/.hg/strip-backup/883aadbbf309-efc55adc-backup.hg
> +  $ hg log -G
> +  @  changeset:   0:c1863a3840c6
> +     tag:         tip
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     root
> +
> +  $ hg log -G --hidden
> +  o  changeset:   1:883aadbbf309
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     unbundletesting
> +  |
> +  @  changeset:   0:c1863a3840c6
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     root
> +
> +  $ hg unbundle .hg/strip-backup/883aadbbf309-efc55adc-backup.hg
> +  adding changesets
> +  adding manifests
> +  adding file changes
> +  added 0 changesets with 0 changes to 1 files
> +  (run 'hg update' to get a working copy)
> +  $ hg log -G
> +  o  changeset:   1:883aadbbf309
> +  |  tag:         tip
> +  |  user:        test
> +  |  date:        Thu Jan 01 00:00:00 1970 +0000
> +  |  summary:     unbundletesting
> +  |
> +  @  changeset:   0:c1863a3840c6
> +     user:        test
> +     date:        Thu Jan 01 00:00:00 1970 +0000
> +     summary:     root
> +
>

Patch

diff --git a/hgext/strip.py b/hgext/strip.py
--- a/hgext/strip.py
+++ b/hgext/strip.py
@@ -76,7 +76,8 @@  def _findupdatetarget(repo, nodes):
 
     return unode
 
-def strip(ui, repo, revs, update=True, backup=True, force=None, bookmarks=None):
+def strip(ui, repo, revs, update=True, backup=True, force=None, bookmarks=None,
+          soft=False):
     with repo.wlock(), repo.lock():
 
         if update:
@@ -85,7 +86,10 @@  def strip(ui, repo, revs, update=True, b
             hg.clean(repo, urev)
             repo.dirstate.write(repo.currenttransaction())
 
-        repair.strip(ui, repo, revs, backup)
+        if soft:
+            repair.softstrip(ui, repo, revs, backup)
+        else:
+            repair.strip(ui, repo, revs, backup)
 
         repomarks = repo._bookmarks
         if bookmarks:
@@ -110,7 +114,10 @@  def strip(ui, repo, revs, update=True, b
           ('k', 'keep', None, _("do not modify working directory during "
                                 "strip")),
           ('B', 'bookmark', [], _("remove revs only reachable from given"
-                                  " bookmark"), _('BOOKMARK'))],
+                                  " bookmark"), _('BOOKMARK')),
+          ('', 'soft', None,
+          _("simply drop changesets from visible history (EXPERIMENTAL)")),
+         ],
           _('hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
           helpcategory=command.CATEGORY_MAINTENANCE)
 def stripcmd(ui, repo, *revs, **opts):
@@ -235,6 +242,7 @@  def stripcmd(ui, repo, *revs, **opts):
 
 
         strip(ui, repo, revs, backup=backup, update=update,
-              force=opts.get('force'), bookmarks=bookmarks)
+              force=opts.get('force'), bookmarks=bookmarks,
+              soft=opts['soft'])
 
     return 0
diff --git a/mercurial/repair.py b/mercurial/repair.py
--- a/mercurial/repair.py
+++ b/mercurial/repair.py
@@ -252,6 +252,24 @@  def strip(ui, repo, nodelist, backup=Tru
     # extensions can use it
     return backupfile
 
+def softstrip(ui, repo, nodelist, backup=True, topic='backup'):
+    """perform a "soft" strip using the archived phase"""
+    tostrip = [c.node() for c in repo.set('sort(%ln::)', nodelist)]
+    if not tostrip:
+        return None
+
+    newbmtarget, updatebm = _bookmarkmovements(repo, tostrip)
+    if backup:
+        node = tostrip[0]
+        backupfile = _createstripbackup(repo, tostrip, node, topic)
+
+    with repo.transaction('strip') as tr:
+        phases.retractboundary(repo, tr, phases.archived, tostrip)
+        bmchanges = [(m, repo[newbmtarget].node()) for m in updatebm]
+        repo._bookmarks.applychanges(repo, tr, bmchanges)
+    return backupfile
+
+
 def _bookmarkmovements(repo, tostrip):
     # compute necessary bookmark movement
     bm = repo._bookmarks
diff --git a/tests/test-phase-archived.t b/tests/test-phase-archived.t
new file mode 100644
--- /dev/null
+++ b/tests/test-phase-archived.t
@@ -0,0 +1,77 @@ 
+=========================================================
+Test features and behaviors related to the archived phase
+=========================================================
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > internal-phase=yes
+  > [extensions]
+  > strip=
+  > [experimental]
+  > EOF
+
+  $ hg init repo
+  $ cd repo
+  $ echo  root > a
+  $ hg add a
+  $ hg ci -m 'root'
+
+Test that bundle can unarchive a changeset
+------------------------------------------
+
+  $ echo foo >> a
+  $ hg st
+  M a
+  $ hg ci -m 'unbundletesting'
+  $ hg log -G
+  @  changeset:   1:883aadbbf309
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     unbundletesting
+  |
+  o  changeset:   0:c1863a3840c6
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     root
+  
+  $ hg strip --soft --rev '.'
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/883aadbbf309-efc55adc-backup.hg
+  $ hg log -G
+  @  changeset:   0:c1863a3840c6
+     tag:         tip
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     root
+  
+  $ hg log -G --hidden
+  o  changeset:   1:883aadbbf309
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     unbundletesting
+  |
+  @  changeset:   0:c1863a3840c6
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     root
+  
+  $ hg unbundle .hg/strip-backup/883aadbbf309-efc55adc-backup.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 0 changesets with 0 changes to 1 files
+  (run 'hg update' to get a working copy)
+  $ hg log -G
+  o  changeset:   1:883aadbbf309
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     unbundletesting
+  |
+  @  changeset:   0:c1863a3840c6
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     root
+