Patchwork D6987: strip: move strip extension to core

login
register
mail settings
Submitter phabricator
Date Oct. 5, 2019, 7:49 p.m.
Message ID <differential-rev-PHID-DREV-mlxhxdlyx73lnvt4t4u3-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/41996/
State New
Headers show

Comments

phabricator - Oct. 5, 2019, 7:49 p.m.
navaneeth.suresh created this revision.
Herald added a reviewer: durin42.
Herald added a reviewer: durin42.
Herald added subscribers: mercurial-devel, mjpieters.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Until now, `strip` was bootstrapped as an extension. This patch adds
  `strip` on core.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

AFFECTED FILES
  contrib/win32/mercurial.ini
  hgext/mq.py
  hgext/strip.py
  mercurial/commands.py
  mercurial/extensions.py
  mercurial/strip.py
  tests/remotefilelog-library.sh
  tests/test-backout.t
  tests/test-branches.t
  tests/test-bundle-phases.t
  tests/test-commandserver.t
  tests/test-completion.t
  tests/test-convert-svn-sink.t
  tests/test-copy-move-merge.t
  tests/test-debugcommands.t
  tests/test-extension.t
  tests/test-flagprocessor.t
  tests/test-generaldelta.t
  tests/test-globalopts.t
  tests/test-graft.t
  tests/test-help-hide.t
  tests/test-help.t
  tests/test-hgweb-bundle.t
  tests/test-hgweb-json.t
  tests/test-histedit-arguments.t
  tests/test-histedit-edit.t
  tests/test-histedit-obsolete.t
  tests/test-hook.t
  tests/test-i18n.t
  tests/test-import-bypass.t
  tests/test-import.t
  tests/test-lfs.t
  tests/test-narrow-strip.t
  tests/test-obsolete.t
  tests/test-phase-archived.t
  tests/test-phases-exchange.t
  tests/test-pull-update.t
  tests/test-push-http.t
  tests/test-push.t
  tests/test-rebase-inmemory.t
  tests/test-rebase-obsolete.t
  tests/test-single-head.t
  tests/test-sparse-clear.t
  tests/test-sparse-clone.t
  tests/test-sparse-fsmonitor.t
  tests/test-sparse-import.t
  tests/test-sparse-merges.t
  tests/test-sparse-profiles.t
  tests/test-sparse-verbose-json.t
  tests/test-sparse.t
  tests/test-ssh-proto-unbundle.t
  tests/test-strip.t
  tests/test-subrepo-missing.t
  tests/test-subrepo.t
  tests/test-tags.t
  tests/test-template-map.t
  tests/test-transplant.t
  tests/test-treemanifest.t
  tests/testlib/exchange-obsmarker-util.sh
  tests/testlib/push-checkheads-util.sh

CHANGE DETAILS




To: navaneeth.suresh, durin42, #hg-reviewers
Cc: mjpieters, mercurial-devel
phabricator - Oct. 5, 2019, 7:55 p.m.
This revision now requires changes to proceed.
durin42 added inline comments.
durin42 requested changes to this revision.

INLINE COMMENTS

> commands.py:5646
>  
> +@command("strip",
> +    [

debugstrip please (and put it in debugcommands)

and Gregory points out we need a strip extension that preserves the old `strip` name for users that are used to that

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6987/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

To: navaneeth.suresh, durin42, #hg-reviewers
Cc: mjpieters, mercurial-devel
phabricator - Oct. 5, 2019, 8:10 p.m.
navaneeth.suresh added inline comments.

INLINE COMMENTS

> durin42 wrote in commands.py:5646
> debugstrip please (and put it in debugcommands)
> 
> and Gregory points out we need a strip extension that preserves the old `strip` name for users that are used to that

i see. do i need to replace `strip` by `debugstrip` on every occurrence in tests? otherwise, i would have to revert the changes in the tests and keep the extension as it is.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6987/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

To: navaneeth.suresh, durin42, #hg-reviewers
Cc: mjpieters, mercurial-devel
phabricator - Oct. 5, 2019, 9:02 p.m.
durin42 added inline comments.

INLINE COMMENTS

> navaneeth.suresh wrote in commands.py:5646
> i see. do i need to replace `strip` by `debugstrip` on every occurrence in tests? otherwise, i would have to revert the changes in the tests and keep the extension as it is.

One or two is fine, you can leave the majority on the legacy extension flavor. We want some using each so we don't have to add a new test to test one of them.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6987/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

To: navaneeth.suresh, durin42, #hg-reviewers
Cc: mjpieters, mercurial-devel
phabricator - Oct. 6, 2019, 5:52 p.m.
navaneeth.suresh added a comment.


  @durin42 Done,

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6987/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

To: navaneeth.suresh, durin42, #hg-reviewers
Cc: mjpieters, mercurial-devel
phabricator - Oct. 8, 2019, 1:06 a.m.
marmoute added inline comments.

INLINE COMMENTS

> strip.py:1-47
>  """strip changesets and their descendants from history
>  
> -This extension allows you to strip changesets and all their descendants from the
> -repository. See the command help for details.
> +strip extension has been renamed to debugstrip and moved to core. However,
> +this extension is to preserve the old `strip` name forusers that are used
> +to that.
>  """
> +

Why do we need that much code to remain into `hgext/strip.py`. What I would expect to see is a very small file that only register debugstrip as `strip`.

> strip.py:24-34
> +    [
> +     ('r', 'rev', [], _('strip specified revision (optional, can specify '
> +                        'revisions without this option)'), _('REV')),
> +     ('f', 'force', None, _('force removal of changesets, discard uncommitted'
> +                            ' changes (no backup)')),
> +     ('', 'no-backup', None, _('do not save backup bundle')),
> +     ('', 'nobackup', None, _('do not save backup bundle (DEPRECATED)')),

What is going on here? IT seems like the same code with a different formatting.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6987/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

To: navaneeth.suresh, durin42, #hg-reviewers
Cc: marmoute, mjpieters, mercurial-devel
phabricator - Oct. 9, 2019, 1:56 p.m.
pulkit added inline comments.

INLINE COMMENTS

> strip.py:3
>  
> -This extension allows you to strip changesets and all their descendants from the
> -repository. See the command help for details.
> +strip extension has been renamed to debugstrip and moved to core. However,
> +this extension is to preserve the old `strip` name forusers that are used

the documentation should be left as it is.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6987/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6987

To: navaneeth.suresh, durin42, #hg-reviewers
Cc: pulkit, marmoute, mjpieters, mercurial-devel

Patch

diff --git a/tests/testlib/push-checkheads-util.sh b/tests/testlib/push-checkheads-util.sh
--- a/tests/testlib/push-checkheads-util.sh
+++ b/tests/testlib/push-checkheads-util.sh
@@ -9,10 +9,6 @@ 
 # non publishing server
 publish=False
 
-[extensions]
-# we need to strip some changeset for some test cases
-strip=
-
 [experimental]
 # enable evolution
 evolution=all
diff --git a/tests/testlib/exchange-obsmarker-util.sh b/tests/testlib/exchange-obsmarker-util.sh
--- a/tests/testlib/exchange-obsmarker-util.sh
+++ b/tests/testlib/exchange-obsmarker-util.sh
@@ -28,10 +28,6 @@ 
 # enable evolution
 evolution=true
 
-[extensions]
-# we need to strip some changeset for some test cases
-hgext.strip=
-
 [devel]
 strip-obsmarkers = no
 
diff --git a/tests/test-treemanifest.t b/tests/test-treemanifest.t
--- a/tests/test-treemanifest.t
+++ b/tests/test-treemanifest.t
@@ -316,7 +316,7 @@ 
 
   $ hg st --change tip
   M dir1/a
-  $ hg --config extensions.strip= strip tip
+  $ hg strip tip
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg
   $ hg debugindex --dir dir1
@@ -343,7 +343,7 @@ 
   added 1 changesets with 1 changes to 1 files
   new changesets 51cfd7b1e13b (1 drafts)
   (run 'hg update' to get a working copy)
-  $ hg --config extensions.strip= strip tip
+  $ hg strip tip
   saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/*-backup.hg (glob)
   $ hg unbundle -q .hg/strip-backup/*
   $ hg debugindex --dir dir1
@@ -897,4 +897,4 @@ 
   $ hg log -r 'tip + tip^' -T '{manifest}\n'
   1:678d3574b88c
   1:678d3574b88c
-  $ hg --config extensions.strip= strip -r . -q
+  $ hg strip -r . -q
diff --git a/tests/test-transplant.t b/tests/test-transplant.t
--- a/tests/test-transplant.t
+++ b/tests/test-transplant.t
@@ -660,7 +660,7 @@ 
 
 test interactive transplant
 
-  $ hg --config extensions.strip= -q strip 0
+  $ hg -q strip 0
   $ hg -R ../t log -G --template "{rev}:{node|short}"
   @  4:a53251cdf717
   |
diff --git a/tests/test-template-map.t b/tests/test-template-map.t
--- a/tests/test-template-map.t
+++ b/tests/test-template-map.t
@@ -294,7 +294,7 @@ 
 Remove commit with empty commit message, so as to not pollute further
 tests.
 
-  $ hg --config extensions.strip= strip -q .
+  $ hg strip -q .
 
 Revision with no copies (used to print a traceback):
 
diff --git a/tests/test-tags.t b/tests/test-tags.t
--- a/tests/test-tags.t
+++ b/tests/test-tags.t
@@ -407,7 +407,7 @@ 
   $ f --size .hg/cache/hgtagsfnodes1
   .hg/cache/hgtagsfnodes1: size=168
 
-  $ hg -q --config extensions.strip= strip -r 6 --no-backup
+  $ hg -q strip -r 6 --no-backup
 #endif
 
 Stripping doesn't truncate the tags cache until new data is available
@@ -420,7 +420,7 @@ 
   $ f --size .hg/cache/hgtagsfnodes1
   .hg/cache/hgtagsfnodes1: size=144
 
-  $ hg -q --config extensions.strip= strip -r 5 --no-backup
+  $ hg -q strip -r 5 --no-backup
   $ hg tags
   tip                                4:0c192d7d5e6b
   bar                                1:78391a272241
@@ -452,7 +452,7 @@ 
   $ f --size .hg/cache/hgtagsfnodes1
   .hg/cache/hgtagsfnodes1: size=144
 
-  $ hg -q --config extensions.strip= strip -r 5 --no-backup
+  $ hg -q strip -r 5 --no-backup
 
 Test tag removal:
 
diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t
--- a/tests/test-subrepo.t
+++ b/tests/test-subrepo.t
@@ -465,10 +465,6 @@ 
 
   $ hg co -C 10
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ cat >> $HGRCPATH <<EOF
-  > [extensions]
-  > strip=
-  > EOF
   $ hg strip -r 11:15
   saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
 
diff --git a/tests/test-subrepo-missing.t b/tests/test-subrepo-missing.t
--- a/tests/test-subrepo-missing.t
+++ b/tests/test-subrepo-missing.t
@@ -108,7 +108,7 @@ 
 verify will warn if locked-in subrepo revisions are hidden or missing
 
   $ hg ci -m "amended subrepo (again)"
-  $ hg --config extensions.strip= --hidden strip -R subrepo -qr 'tip' --config devel.strip-obsmarkers=no
+  $ hg --hidden strip -R subrepo -qr 'tip' --config devel.strip-obsmarkers=no
   $ hg verify
   checking changesets
   checking manifests
diff --git a/tests/test-strip.t b/tests/test-strip.t
--- a/tests/test-strip.t
+++ b/tests/test-strip.t
@@ -1,5 +1,4 @@ 
   $ echo "[extensions]" >> $HGRCPATH
-  $ echo "strip=" >> $HGRCPATH
   $ echo "drawdag=$TESTDIR/drawdag.py" >> $HGRCPATH
 
   $ restore() {
@@ -758,8 +757,6 @@ 
   
   strip changesets and all their descendants from the repository
   
-  (use 'hg help -e strip' to show help for the strip extension)
-  
   options ([+] can be repeated):
   
    -r --rev REV [+]           strip specified revision (optional, can specify
@@ -882,7 +879,7 @@ 
 
 check strip behavior
 
-  $ hg --config extensions.strip= strip 'desc(commitD)' --debug
+  $ hg strip 'desc(commitD)' --debug
   resolving manifests
    branchmerge: False, force: True, partial: False
    ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
diff --git a/tests/test-ssh-proto-unbundle.t b/tests/test-ssh-proto-unbundle.t
--- a/tests/test-ssh-proto-unbundle.t
+++ b/tests/test-ssh-proto-unbundle.t
@@ -11,13 +11,13 @@ 
   >   tip=`hg log -r tip -T '{node}'`
   >   echo "${commands}" | hg --verbose debugwireproto --localssh --noreadstderr
   >   if [ -n "$1" ]; then
-  >       hg --config extensions.strip= strip --no-backup -r "all() - ::${tip}"
+  >       hg strip --no-backup -r "all() - ::${tip}"
   >   fi
   >   echo ""
   >   echo 'testing ssh2'
   >   echo "${commands}" | HGRCPATH=$TESTTMP/hgrc-sshv2 hg --verbose debugwireproto --localssh --noreadstderr
   >   if [ -n "$1" ]; then
-  >       hg --config extensions.strip= strip --no-backup -r "all() - ::${tip}"
+  >       hg strip --no-backup -r "all() - ::${tip}"
   >   fi
   > }
 
diff --git a/tests/test-sparse.t b/tests/test-sparse.t
--- a/tests/test-sparse.t
+++ b/tests/test-sparse.t
@@ -5,7 +5,6 @@ 
   $ cat > .hg/hgrc <<EOF
   > [extensions]
   > sparse=
-  > strip=
   > EOF
 
   $ echo a > show
diff --git a/tests/test-sparse-verbose-json.t b/tests/test-sparse-verbose-json.t
--- a/tests/test-sparse-verbose-json.t
+++ b/tests/test-sparse-verbose-json.t
@@ -5,7 +5,6 @@ 
   $ cat > .hg/hgrc <<EOF
   > [extensions]
   > sparse=
-  > strip=
   > EOF
 
   $ echo a > show
diff --git a/tests/test-sparse-profiles.t b/tests/test-sparse-profiles.t
--- a/tests/test-sparse-profiles.t
+++ b/tests/test-sparse-profiles.t
@@ -6,7 +6,6 @@ 
   > [extensions]
   > sparse=
   > purge=
-  > strip=
   > rebase=
   > EOF
 
diff --git a/tests/test-sparse-merges.t b/tests/test-sparse-merges.t
--- a/tests/test-sparse-merges.t
+++ b/tests/test-sparse-merges.t
@@ -49,7 +49,7 @@ 
 Test merging things outside of the sparse checkout that are not in the working
 copy
 
-  $ hg strip -q -r . --config extensions.strip=
+  $ hg strip -q -r .
   $ hg up -q feature
   $ touch branchonly
   $ hg ci -Aqm 'add branchonly'
diff --git a/tests/test-sparse-import.t b/tests/test-sparse-import.t
--- a/tests/test-sparse-import.t
+++ b/tests/test-sparse-import.t
@@ -6,7 +6,6 @@ 
   > [extensions]
   > sparse=
   > purge=
-  > strip=
   > rebase=
   > EOF
 
diff --git a/tests/test-sparse-fsmonitor.t b/tests/test-sparse-fsmonitor.t
--- a/tests/test-sparse-fsmonitor.t
+++ b/tests/test-sparse-fsmonitor.t
@@ -9,7 +9,6 @@ 
   $ cat > .hg/hgrc <<EOF
   > [extensions]
   > sparse=
-  > strip=
   > EOF
 
 Test fsmonitor integration (if available)
diff --git a/tests/test-sparse-clone.t b/tests/test-sparse-clone.t
--- a/tests/test-sparse-clone.t
+++ b/tests/test-sparse-clone.t
@@ -7,7 +7,6 @@ 
   > [extensions]
   > sparse=
   > purge=
-  > strip=
   > rebase=
   > EOF
 
diff --git a/tests/test-sparse-clear.t b/tests/test-sparse-clear.t
--- a/tests/test-sparse-clear.t
+++ b/tests/test-sparse-clear.t
@@ -6,7 +6,6 @@ 
   > [extensions]
   > sparse=
   > purge=
-  > strip=
   > rebase=
   > EOF
 
diff --git a/tests/test-single-head.t b/tests/test-single-head.t
--- a/tests/test-single-head.t
+++ b/tests/test-single-head.t
@@ -197,7 +197,7 @@ 
 
 actual stripping
 
-  $ hg strip --config extensions.strip= --rev 'desc("c_dH0")'
+  $ hg strip --rev 'desc("c_dH0")'
   saved backup bundle to $TESTTMP/client/.hg/strip-backup/fe47ea669cea-a41bf5a9-backup.hg
 
 Test that closing heads are ignored by default
diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
--- a/tests/test-rebase-obsolete.t
+++ b/tests/test-rebase-obsolete.t
@@ -15,7 +15,6 @@ 
   > [extensions]
   > rebase=
   > drawdag=$TESTDIR/drawdag.py
-  > strip=
   > EOF
 
 Setup rebase canonical repo
@@ -1560,7 +1559,7 @@ 
   1 new orphan changesets
 
 This strip seems to be the key to avoid an early divergence warning.
-  $ hg --config extensions.strip= --hidden strip -qr H
+  $ hg --hidden strip -qr H
   1 new orphan changesets
 
   $ hg rebase -b 'desc("D")' -d 'desc("J")'
diff --git a/tests/test-rebase-inmemory.t b/tests/test-rebase-inmemory.t
--- a/tests/test-rebase-inmemory.t
+++ b/tests/test-rebase-inmemory.t
@@ -6,7 +6,6 @@ 
   > amend=
   > rebase=
   > debugdrawdag=$TESTDIR/drawdag.py
-  > strip=
   > [rebase]
   > experimental.inmemory=1
   > [diff]
diff --git a/tests/test-push.t b/tests/test-push.t
--- a/tests/test-push.t
+++ b/tests/test-push.t
@@ -291,8 +291,8 @@ 
   wlock: free
   added 1 changesets with 1 changes to 1 files
 
-  $ hg --cwd 1 --config extensions.strip= strip tip -q
-  $ hg --cwd 2 --config extensions.strip= strip tip -q
+  $ hg --cwd 1 strip tip -q
+  $ hg --cwd 2 strip tip -q
   $ hg --cwd 3 push ../2 # bundle2+
   pushing to ../2
   searching for changes
diff --git a/tests/test-push-http.t b/tests/test-push-http.t
--- a/tests/test-push-http.t
+++ b/tests/test-push-http.t
@@ -370,7 +370,7 @@ 
   % serve errors
 #endif
 
-  $ hg --config extensions.strip= strip -r 1:
+  $ hg strip -r 1:
   saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
 
 Now do a variant of the above, except on a non-publishing repository
@@ -483,7 +483,7 @@ 
   % serve errors
 #endif
 
-  $ hg --config extensions.strip= strip -r 1:
+  $ hg strip -r 1:
   saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
 
 #if bundle2
diff --git a/tests/test-pull-update.t b/tests/test-pull-update.t
--- a/tests/test-pull-update.t
+++ b/tests/test-pull-update.t
@@ -30,7 +30,7 @@ 
   1 local changesets published
   abort: uncommitted changes
   [255]
-  $ hg --config extensions.strip= strip --no-backup tip
+  $ hg strip --no-backup tip
   $ hg co -qC tip
 
 Should not update to the other topological branch:
diff --git a/tests/test-phases-exchange.t b/tests/test-phases-exchange.t
--- a/tests/test-phases-exchange.t
+++ b/tests/test-phases-exchange.t
@@ -836,7 +836,7 @@ 
 
 (reset some stat on remote repo to avoid confusing other tests)
 
-  $ hg -R ../alpha --config extensions.strip= strip --no-backup 967b449fbc94
+  $ hg -R ../alpha strip --no-backup 967b449fbc94
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg phase --force --draft b740e3e5c05d 967b449fbc94
   test-debug-phase: move rev 8: 0 -> 1
diff --git a/tests/test-phase-archived.t b/tests/test-phase-archived.t
--- a/tests/test-phase-archived.t
+++ b/tests/test-phase-archived.t
@@ -5,8 +5,6 @@ 
   $ cat << EOF >> $HGRCPATH
   > [format]
   > internal-phase=yes
-  > [extensions]
-  > strip=
   > [experimental]
   > EOF
 
diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t
--- a/tests/test-obsolete.t
+++ b/tests/test-obsolete.t
@@ -1297,7 +1297,7 @@ 
   tiptag                             2:323a9c3ddd91
   tip                                2:323a9c3ddd91
   visible                            1:29f0c6921ddd
-  $ hg --config extensions.strip= strip -r tip --no-backup
+  $ hg strip -r tip --no-backup
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg tags
   visible                            1:29f0c6921ddd
@@ -1522,7 +1522,7 @@ 
   o  0:a78f55e5508c (draft) [ ] 0
   
 
-  $ hg strip --hidden -r 2 --config extensions.strip= --config devel.strip-obsmarkers=no
+  $ hg strip --hidden -r 2 --config devel.strip-obsmarkers=no
   saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e008cf283490-ede36964-backup.hg
   $ hg debugobsolete
   e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
@@ -1573,7 +1573,7 @@ 
 
 Testing that strip remove markers:
 
-  $ hg strip -r 1 --config extensions.strip=
+  $ hg strip -r 1
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e016b03fd86f-65ede734-backup.hg
   $ hg debugobsolete
@@ -1681,7 +1681,7 @@ 
   $ hg bundle -r . --base .~1 ../bundle-2.hg
   1 changesets found
   $ getid .
-  $ hg --config extensions.strip= strip -r .
+  $ hg strip -r .
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/tmpe/issue4845/doindexrev/.hg/strip-backup/9bc153528424-ee80edd4-backup.hg
   $ hg debugobsolete 9bc153528424ea266d13e57f9ff0d799dfe61e4b
diff --git a/tests/test-narrow-strip.t b/tests/test-narrow-strip.t
--- a/tests/test-narrow-strip.t
+++ b/tests/test-narrow-strip.t
@@ -54,10 +54,6 @@ 
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd narrow
-  $ cat >> $HGRCPATH <<EOF
-  > [extensions]
-  > strip=
-  > EOF
 
 Can strip and recover changesets affecting only files within narrow spec
 
diff --git a/tests/test-lfs.t b/tests/test-lfs.t
--- a/tests/test-lfs.t
+++ b/tests/test-lfs.t
@@ -419,7 +419,7 @@ 
        * (changelog) (glob)
        * (manifests) (glob)
       * a (glob)
-  $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
+  $ hg strip -r 2 --no-backup --force -q
   $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
   5 branching
   diff --git a/a b/a
diff --git a/tests/test-import.t b/tests/test-import.t
--- a/tests/test-import.t
+++ b/tests/test-import.t
@@ -121,7 +121,7 @@ 
   d8804f3f5396d800812f579c8452796a5993bdb2
   $ hg --cwd b export -o ../empty-log.diff .
   $ hg --cwd b update -q -C ".^1"
-  $ hg --cwd b --config extensions.strip= strip -q tip
+  $ hg --cwd b strip -q tip
   $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
   applying ../empty-log.diff
   $ hg --cwd b tip -T "{node}\n"
@@ -467,7 +467,7 @@ 
   parent: 1
 
   $ hg --cwd b update -q -C 0
-  $ hg --cwd b --config extensions.strip= strip -q 1
+  $ hg --cwd b strip -q 1
 
 Test visibility of in-memory dirstate changes inside transaction to
 external process
diff --git a/tests/test-import-bypass.t b/tests/test-import-bypass.t
--- a/tests/test-import-bypass.t
+++ b/tests/test-import-bypass.t
@@ -183,7 +183,7 @@ 
   @@ -1,1 +1,2 @@
    cc
   +cc
-  $ hg -q --config extensions.strip= strip .
+  $ hg -q strip .
 
 Test unsupported combinations
 
@@ -323,7 +323,7 @@ 
   1b77bc7d1db9f0e7f1716d515b630516ab386c89
   $ hg export -o ../empty-log.diff .
   $ hg update -q -C ".^1"
-  $ hg --config extensions.strip= strip -q tip
+  $ hg strip -q tip
   $ HGEDITOR=cat hg import --exact --bypass ../empty-log.diff
   applying ../empty-log.diff
   $ hg tip -T "{node}\n"
diff --git a/tests/test-i18n.t b/tests/test-i18n.t
--- a/tests/test-i18n.t
+++ b/tests/test-i18n.t
@@ -37,6 +37,7 @@ 
   Befehle:
   
    pull   Ruft \xc3\x84nderungen von der angegebenen Quelle ab (esc)
+   strip  Entfernt \xc3\x84nderungss\xc3\xa4tze und alle Nachfahren aus dem Projektarchiv (esc)
    update Aktualisiert das Arbeitsverzeichnis (oder wechselt die Version)
 
 #endif
diff --git a/tests/test-hook.t b/tests/test-hook.t
--- a/tests/test-hook.t
+++ b/tests/test-hook.t
@@ -1251,7 +1251,7 @@ 
   > [hooks]
   > pretxnclose.error = exit 1
   > EOF
-  $ hg strip -r 0 --config extensions.strip=
+  $ hg strip -r 0
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   saved backup bundle to * (glob)
   transaction abort!
diff --git a/tests/test-histedit-obsolete.t b/tests/test-histedit-obsolete.t
--- a/tests/test-histedit-obsolete.t
+++ b/tests/test-histedit-obsolete.t
@@ -79,7 +79,7 @@ 
   ? plan
   $ hg commit --amend -X . -m XXXXXX
   $ hg commit --amend -X . -m b2
-  $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
+  $ hg --hidden strip 'desc(XXXXXX)' --no-backup
   $ hg histedit --continue
   $ hg log -G
   @  8:273c1f3b8626 c
diff --git a/tests/test-histedit-edit.t b/tests/test-histedit-edit.t
--- a/tests/test-histedit-edit.t
+++ b/tests/test-histedit-edit.t
@@ -3,7 +3,6 @@ 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > histedit=
-  > strip=
   > mockmakedate = $TESTDIR/mockmakedate.py
   > EOF
 
diff --git a/tests/test-histedit-arguments.t b/tests/test-histedit-arguments.t
--- a/tests/test-histedit-arguments.t
+++ b/tests/test-histedit-arguments.t
@@ -175,7 +175,7 @@ 
   ~
 
   $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
-  $ hg strip -q -r f5ed --config extensions.strip=
+  $ hg strip -q -r f5ed
   $ hg up -q 08d98a8350f3
 
 Test that missing revisions are detected
@@ -330,7 +330,7 @@ 
 
 Test --continue with --keep
 
-  $ hg strip -q -r . --config extensions.strip=
+  $ hg strip -q -r .
   $ hg histedit '.^' -q --keep --commands - << EOF
   > edit eb57da33312f 2 three
   > pick f3cfcca30c44 4 x
diff --git a/tests/test-hgweb-json.t b/tests/test-hgweb-json.t
--- a/tests/test-hgweb-json.t
+++ b/tests/test-hgweb-json.t
@@ -2069,6 +2069,10 @@ 
         "topic": "shelve"
       },
       {
+        "summary": "strip changesets and all their descendants from the repository",
+        "topic": "strip"
+      },
+      {
         "summary": "add one or more tags for the current or given revision",
         "topic": "tag"
       },
diff --git a/tests/test-hgweb-bundle.t b/tests/test-hgweb-bundle.t
--- a/tests/test-hgweb-bundle.t
+++ b/tests/test-hgweb-bundle.t
@@ -2,10 +2,6 @@ 
 
   $ hg init server
   $ cd server
-  $ cat >> .hg/hgrc << EOF
-  > [extensions]
-  > strip=
-  > EOF
 
   $ echo 1 > foo
   $ hg commit -A -m 'first'
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -130,6 +130,7 @@ 
   
    manifest      output the current or given revision of the project manifest
    recover       roll back an interrupted transaction
+   strip         strip changesets and all their descendants from the repository
    verify        verify the integrity of the repository
   
   Help:
@@ -258,6 +259,7 @@ 
   
    manifest      output the current or given revision of the project manifest
    recover       roll back an interrupted transaction
+   strip         strip changesets and all their descendants from the repository
    verify        verify the integrity of the repository
   
   Help:
@@ -379,7 +381,6 @@ 
        relink        recreates hardlinks between repository clones
        schemes       extend schemes with shortcuts to repository swarms
        share         share a common history between several working directories
-       strip         strip changesets and their descendants from history
        transplant    command to transplant changesets from another branch
        win32mbcs     allow the use of MBCS paths with problematic encodings
        zeroconf      discover and advertise repositories on the local network
@@ -2733,6 +2734,13 @@ 
   save and set aside changes from the working directory
   </td></tr>
   <tr><td>
+  <a href="/help/strip">
+  strip
+  </a>
+  </td><td>
+  strip changesets and all their descendants from the repository
+  </td></tr>
+  <tr><td>
   <a href="/help/tag">
   tag
   </a>
diff --git a/tests/test-help-hide.t b/tests/test-help-hide.t
--- a/tests/test-help-hide.t
+++ b/tests/test-help-hide.t
@@ -78,6 +78,7 @@ 
   
    manifest      output the current or given revision of the project manifest
    recover       roll back an interrupted transaction
+   strip         strip changesets and all their descendants from the repository
    verify        verify the integrity of the repository
   
   Help:
@@ -214,6 +215,7 @@ 
   
    manifest      output the current or given revision of the project manifest
    recover       roll back an interrupted transaction
+   strip         strip changesets and all their descendants from the repository
    verify        verify the integrity of the repository
   
   Help:
diff --git a/tests/test-graft.t b/tests/test-graft.t
--- a/tests/test-graft.t
+++ b/tests/test-graft.t
@@ -64,7 +64,7 @@ 
   $ hg st --change .
   M d
 
-  $ hg -q strip . --config extensions.strip=
+  $ hg -q strip .
 
 Test --base for collapsing changesets 2 and 3, thus getting both b and c
 
@@ -78,7 +78,7 @@ 
   A c
   R a
 
-  $ hg -q strip . --config extensions.strip=
+  $ hg -q strip .
 
 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
 
@@ -307,7 +307,7 @@ 
   $ echo c >> e
   $ hg ci -mtest
 
-  $ hg strip . --config extensions.strip=
+  $ hg strip .
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
 
@@ -877,7 +877,7 @@ 
 
   $ hg up -qC 7
   $ hg tag -l -r 13 tmp
-  $ hg --config extensions.strip= strip 2
+  $ hg strip 2
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
   $ hg graft tmp
   skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
diff --git a/tests/test-globalopts.t b/tests/test-globalopts.t
--- a/tests/test-globalopts.t
+++ b/tests/test-globalopts.t
@@ -374,6 +374,7 @@ 
   
    manifest      output the current or given revision of the project manifest
    recover       roll back an interrupted transaction
+   strip         strip changesets and all their descendants from the repository
    verify        verify the integrity of the repository
   
   Help:
@@ -506,6 +507,7 @@ 
   
    manifest      output the current or given revision of the project manifest
    recover       roll back an interrupted transaction
+   strip         strip changesets and all their descendants from the repository
    verify        verify the integrity of the repository
   
   Help:
diff --git a/tests/test-generaldelta.t b/tests/test-generaldelta.t
--- a/tests/test-generaldelta.t
+++ b/tests/test-generaldelta.t
@@ -146,7 +146,7 @@ 
         1       1        2        0    prev         61         86        120   1.39535       120         0    0.00000
         2       1        2        0      p2         62        301        121   0.40199       182        61    0.50413
 
-  $ hg strip -q -r . --config extensions.strip=
+  $ hg strip -q -r .
 
 - Verify aggressive merge uses p2 (commit 0) as delta parent
   $ hg up -q -C 1
@@ -159,7 +159,7 @@ 
         2       1        2        0      p2         62        301        121   0.40199       182        61    0.50413
 
 Test that strip bundle use bundle2
-  $ hg --config extensions.strip= strip .
+  $ hg strip .
   0 files updated, 0 files merged, 5 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg
   $ hg debugbundle .hg/strip-backup/*
diff --git a/tests/test-flagprocessor.t b/tests/test-flagprocessor.t
--- a/tests/test-flagprocessor.t
+++ b/tests/test-flagprocessor.t
@@ -242,7 +242,7 @@ 
 #if repobundlerepo
   $ hg bundle --base 1 bundle.hg
   4 changesets found
-  $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
+  $ hg strip -r 2 --no-backup --force -q
   $ hg -R bundle.hg log --stat -T '{rev} {desc}\n' base64
   5 branching
    base64 |  2 +-
diff --git a/tests/test-extension.t b/tests/test-extension.t
--- a/tests/test-extension.t
+++ b/tests/test-extension.t
@@ -1019,7 +1019,6 @@ 
 #if no-extraextensions
   $ hg debugextensions
   mq
-  strip
 #endif
 
 For extensions, which name matches one of its commands, help
@@ -1546,7 +1545,7 @@ 
   $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
   $ rm -f throw.pyc throw.pyo
   $ rm -Rf __pycache__
-  $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
+  $ hg version -v --config extensions.throw=throw.py
   Mercurial Distributed SCM (version *) (glob)
   (see https://mercurial-scm.org for more information)
   
@@ -1557,15 +1556,13 @@ 
   Enabled extensions:
   
     throw  external  1.twentythree
-    strip  internal  
 
   $ hg version -q --config extensions.throw=throw.py
   Mercurial Distributed SCM (version *) (glob)
 
 Test template output:
 
-  $ hg version --config extensions.strip= -T'{extensions}'
-  strip
+  $ hg version -T'{extensions}'
 
 Test JSON output of version:
 
@@ -1585,20 +1582,19 @@ 
    }
   ]
 
-  $ hg version --config extensions.strip= -Tjson
+  $ hg version -Tjson
   [
    {
-    "extensions": [{"bundled": true, "name": "strip", "ver": null}],
+    "extensions": [],
     "ver": "*" (glob)
    }
   ]
 
 Test template output of version:
 
-  $ hg version --config extensions.throw=throw.py --config extensions.strip= \
+  $ hg version --config extensions.throw=throw.py \
   > -T'{extensions % "{name}  {pad(ver, 16)}  ({if(bundled, "internal", "external")})\n"}'
   throw  1.twentythree     (external)
-  strip                    (internal)
 
 Refuse to load extensions with minimum version requirements
 
@@ -1679,7 +1675,6 @@ 
   dudu (untested!)
   mq
   reposetuptest (untested!)
-  strip
 #endif
 
   $ hg clone -U src clone-dst1
diff --git a/tests/test-debugcommands.t b/tests/test-debugcommands.t
--- a/tests/test-debugcommands.t
+++ b/tests/test-debugcommands.t
@@ -370,7 +370,7 @@ 
   9 1
   10 2
   11 1
-  $ hg --config extensions.strip= strip --no-backup -r 1
+  $ hg strip --no-backup -r 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Test max chain len
diff --git a/tests/test-copy-move-merge.t b/tests/test-copy-move-merge.t
--- a/tests/test-copy-move-merge.t
+++ b/tests/test-copy-move-merge.t
@@ -79,7 +79,7 @@ 
 
 - next verify copy metadata is lost when disabled
 
-  $ hg strip -r . --config extensions.strip=
+  $ hg strip -r .
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/t/.hg/strip-backup/550bd84c0cd3-fc575957-backup.hg
   $ hg up -qC 2
diff --git a/tests/test-convert-svn-sink.t b/tests/test-convert-svn-sink.t
--- a/tests/test-convert-svn-sink.t
+++ b/tests/test-convert-svn-sink.t
@@ -255,7 +255,7 @@ 
 
   $ hg --cwd a up 5
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg --cwd a --config extensions.strip= strip -r 6
+  $ hg --cwd a strip -r 6
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/bd4f7b7a7067-ed505e42-backup.hg
 
 #endif
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -49,6 +49,7 @@ 
   serve
   shelve
   status
+  strip
   summary
   tag
   tags
@@ -234,7 +235,7 @@ 
 Show an error if we use --options with an ambiguous abbreviation
   $ hg debugcomplete --options s
   hg: command 's' is ambiguous:
-      serve shelve showconfig status summary
+      serve shelve showconfig status strip summary
   [255]
 
 Show all commands + options
@@ -351,6 +352,7 @@ 
   serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
   shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
   status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
+  strip: rev, force, no-backup, nobackup, , keep, bookmark, soft
   summary: remote
   tag: force, local, rev, remove, edit, message, date, user
   tags: template
diff --git a/tests/test-commandserver.t b/tests/test-commandserver.t
--- a/tests/test-commandserver.t
+++ b/tests/test-commandserver.t
@@ -441,7 +441,7 @@ 
   ...     # load _phasecache._phaserevs and _phasesets
   ...     runcommand(server, [b'log', b'-qr', b'draft()'])
   ...     # strip cached revisions by another process
-  ...     os.system('hg --config extensions.strip= strip -q 5')
+  ...     os.system('hg strip -q 5')
   ...     # shouldn't abort by "unknown revision '6'"
   ...     runcommand(server, [b'log', b'-qr', b'draft()'])
   ...     bprint(b'')
diff --git a/tests/test-bundle-phases.t b/tests/test-bundle-phases.t
--- a/tests/test-bundle-phases.t
+++ b/tests/test-bundle-phases.t
@@ -2,7 +2,6 @@ 
   > [experimental]
   > bundle-phases=yes
   > [extensions]
-  > strip=
   > drawdag=$TESTDIR/drawdag.py
   > EOF
 
diff --git a/tests/test-branches.t b/tests/test-branches.t
--- a/tests/test-branches.t
+++ b/tests/test-branches.t
@@ -860,7 +860,7 @@ 
   0090: c9 14 c9 9f 00 00 00 06 cd 21 a8 0b 80 00 00 05 |.........!......|
 cache is updated/truncated when stripping - it is thus very hard to get in a
 situation where the cache is out of sync and the hash check detects it
-  $ hg --config extensions.strip= strip -r tip --nob
+  $ hg strip -r tip --nob
   $ f --size .hg/cache/rbc-revs*
   .hg/cache/rbc-revs-v1: size=152
 
diff --git a/tests/test-backout.t b/tests/test-backout.t
--- a/tests/test-backout.t
+++ b/tests/test-backout.t
@@ -279,7 +279,7 @@ 
 == test visibility to external preupdate hook
 
   $ hg update -q -C 2
-  $ hg --config extensions.strip= strip 3
+  $ hg strip 3
   saved backup bundle to * (glob)
 
   $ cat >> .hg/hgrc <<EOF
@@ -316,7 +316,7 @@ 
 == test visibility to external update hook
 
   $ hg update -q -C 2
-  $ hg --config extensions.strip= strip 3
+  $ hg strip 3
   saved backup bundle to * (glob)
 
   $ cat >> .hg/hgrc <<EOF
diff --git a/tests/remotefilelog-library.sh b/tests/remotefilelog-library.sh
--- a/tests/remotefilelog-library.sh
+++ b/tests/remotefilelog-library.sh
@@ -6,7 +6,6 @@ 
 [extensions]
 remotefilelog=
 rebase=
-strip=
 [ui]
 ssh=python "$TESTDIR/dummyssh"
 [server]
diff --git a/hgext/strip.py b/mercurial/strip.py
rename from hgext/strip.py
rename to mercurial/strip.py
--- a/hgext/strip.py
+++ b/mercurial/strip.py
@@ -12,10 +12,8 @@ 
     error,
     hg,
     lock as lockmod,
-    merge,
     node as nodemod,
     pycompat,
-    registrar,
     repair,
     scmutil,
     util,
@@ -23,8 +21,6 @@ 
 nullid = nodemod.nullid
 release = lockmod.release
 
-cmdtable = {}
-command = registrar.command(cmdtable)
 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
 # be specifying the version(s) of Mercurial they are tested with, or
@@ -40,7 +36,7 @@ 
         cmdutil.checkunfinished(repo, skipmerge=True)
     return s
 
-def _findupdatetarget(repo, nodes):
+def findupdatetarget(repo, nodes):
     unode, p2 = repo.changelog.parents(nodes[0])
     currentbranch = repo[None].branch()
 
@@ -64,7 +60,7 @@ 
 
         if update:
             checklocalchanges(repo, force=force)
-            urev = _findupdatetarget(repo, revs)
+            urev = findupdatetarget(repo, revs)
             hg.clean(repo, urev)
             repo.dirstate.write(repo.currenttransaction())
 
@@ -81,150 +77,3 @@ 
                 repomarks.applychanges(repo, tr, [(b, None) for b in bookmarks])
             for bookmark in sorted(bookmarks):
                 ui.write(_("bookmark '%s' deleted\n") % bookmark)
-
-@command("strip",
-         [
-          ('r', 'rev', [], _('strip specified revision (optional, '
-                               'can specify revisions without this '
-                               'option)'), _('REV')),
-          ('f', 'force', None, _('force removal of changesets, discard '
-                                 'uncommitted changes (no backup)')),
-          ('', 'no-backup', None, _('do not save backup bundle')),
-          ('', 'nobackup', None, _('do not save backup bundle '
-                                   '(DEPRECATED)')),
-          ('n', '', None, _('ignored  (DEPRECATED)')),
-          ('k', 'keep', None, _("do not modify working directory during "
-                                "strip")),
-          ('B', 'bookmark', [], _("remove revs only reachable from given"
-                                  " 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):
-    """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, in which
-    case changes will be discarded.
-
-    If a parent of the working directory is stripped, then the working
-    directory will automatically be updated to the most recent
-    available ancestor of the stripped parent after the operation
-    completes.
-
-    Any stripped changesets are stored in ``.hg/strip-backup`` as a
-    bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
-    be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
-    where BUNDLE is the bundle file created by the strip. Note that
-    the local revision numbers will in general be different after the
-    restore.
-
-    Use the --no-backup option to discard the backup bundle once the
-    operation completes.
-
-    Strip is not a history-rewriting operation and can be used on
-    changesets in the public phase. But if the stripped changesets have
-    been pushed to a remote repository you will likely pull them again.
-
-    Return 0 on success.
-    """
-    opts = pycompat.byteskwargs(opts)
-    backup = True
-    if opts.get('no_backup') or opts.get('nobackup'):
-        backup = False
-
-    cl = repo.changelog
-    revs = list(revs) + opts.get('rev')
-    revs = set(scmutil.revrange(repo, revs))
-
-    with repo.wlock():
-        bookmarks = set(opts.get('bookmark'))
-        if bookmarks:
-            repomarks = repo._bookmarks
-            if not bookmarks.issubset(repomarks):
-                raise error.Abort(_("bookmark '%s' not found") %
-                    ','.join(sorted(bookmarks - set(repomarks.keys()))))
-
-            # If the requested bookmark is not the only one pointing to a
-            # a revision we have to only delete the bookmark and not strip
-            # anything. revsets cannot detect that case.
-            nodetobookmarks = {}
-            for mark, node in repomarks.iteritems():
-                nodetobookmarks.setdefault(node, []).append(mark)
-            for marks in nodetobookmarks.values():
-                if bookmarks.issuperset(marks):
-                    rsrevs = scmutil.bookmarkrevs(repo, marks[0])
-                    revs.update(set(rsrevs))
-            if not revs:
-                with repo.lock(), repo.transaction('bookmark') as tr:
-                    bmchanges = [(b, None) for b in bookmarks]
-                    repomarks.applychanges(repo, tr, bmchanges)
-                for bookmark in sorted(bookmarks):
-                    ui.write(_("bookmark '%s' deleted\n") % bookmark)
-
-        if not revs:
-            raise error.Abort(_('empty revision set'))
-
-        descendants = set(cl.descendants(revs))
-        strippedrevs = revs.union(descendants)
-        roots = revs.difference(descendants)
-
-        # if one of the wdir parent is stripped we'll need
-        # to update away to an earlier revision
-        update = any(p != nullid and cl.rev(p) in strippedrevs
-                     for p in repo.dirstate.parents())
-
-        rootnodes = set(cl.node(r) for r in roots)
-
-        q = getattr(repo, 'mq', None)
-        if q is not None and q.applied:
-            # refresh queue state if we're about to strip
-            # applied patches
-            if cl.rev(repo.lookup('qtip')) in strippedrevs:
-                q.applieddirty = True
-                start = 0
-                end = len(q.applied)
-                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.savedirty()
-
-        revs = sorted(rootnodes)
-        if update and opts.get('keep'):
-            urev = _findupdatetarget(repo, revs)
-            uctx = repo[urev]
-
-            # only reset the dirstate for files that would actually change
-            # between the working context and uctx
-            descendantrevs = repo.revs(b"%d::.", uctx.rev())
-            changedfiles = []
-            for rev in descendantrevs:
-                # blindly reset the files, regardless of what actually changed
-                changedfiles.extend(repo[rev].files())
-
-            # reset files that only changed in the dirstate too
-            dirstate = repo.dirstate
-            dirchanges = [f for f in dirstate if dirstate[f] != 'n']
-            changedfiles.extend(dirchanges)
-
-            repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
-            repo.dirstate.write(repo.currenttransaction())
-
-            # clear resolve state
-            merge.mergestate.clean(repo, repo['.'].node())
-
-            update = False
-
-
-        strip(ui, repo, revs, backup=backup, update=update,
-              force=opts.get('force'), bookmarks=bookmarks,
-              soft=opts['soft'])
-
-    return 0
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -45,6 +45,7 @@ 
     'inotify',
     'hgcia',
     'shelve',
+    'strip',
 }
 
 def extensions(ui=None):
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -59,6 +59,7 @@ 
     server,
     shelve as shelvemod,
     state as statemod,
+    strip as stripmod,
     streamclone,
     tags as tagsmod,
     ui as uimod,
@@ -5642,6 +5643,150 @@ 
         cmdutil.morestatus(repo, fm)
     fm.end()
 
+@command("strip",
+    [
+     ('r', 'rev', [], _('strip specified revision (optional, can specify '
+                        'revisions without this option)'), _('REV')),
+     ('f', 'force', None, _('force removal of changesets, discard uncommitted'
+                            ' changes (no backup)')),
+     ('', 'no-backup', None, _('do not save backup bundle')),
+     ('', 'nobackup', None, _('do not save backup bundle (DEPRECATED)')),
+     ('n', '', None, _('ignored  (DEPRECATED)')),
+     ('k', 'keep', None, _("do not modify working directory during strip")),
+     ('B', 'bookmark', [], _("remove revs only reachable from given bookmark"),
+                           _('BOOKMARK')),
+     ('', 'soft', None,
+     _("simply drop changesets from visible history (EXPERIMENTAL)")),
+    ],
+    _('hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
+    helpcategory=command.CATEGORY_MAINTENANCE)
+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, in which
+    case changes will be discarded.
+
+    If a parent of the working directory is stripped, then the working
+    directory will automatically be updated to the most recent
+    available ancestor of the stripped parent after the operation
+    completes.
+
+    Any stripped changesets are stored in ``.hg/strip-backup`` as a
+    bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
+    be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
+    where BUNDLE is the bundle file created by the strip. Note that
+    the local revision numbers will in general be different after the
+    restore.
+
+    Use the --no-backup option to discard the backup bundle once the
+    operation completes.
+
+    Strip is not a history-rewriting operation and can be used on
+    changesets in the public phase. But if the stripped changesets have
+    been pushed to a remote repository you will likely pull them again.
+
+    Return 0 on success.
+    """
+    opts = pycompat.byteskwargs(opts)
+    backup = True
+    if opts.get('no_backup') or opts.get('nobackup'):
+        backup = False
+
+    cl = repo.changelog
+    revs = list(revs) + opts.get('rev')
+    revs = set(scmutil.revrange(repo, revs))
+
+    with repo.wlock():
+        bookmarks = set(opts.get('bookmark'))
+        if bookmarks:
+            repomarks = repo._bookmarks
+            if not bookmarks.issubset(repomarks):
+                raise error.Abort(_("bookmark '%s' not found") %
+                    ','.join(sorted(bookmarks - set(repomarks.keys()))))
+
+            # If the requested bookmark is not the only one pointing to a
+            # a revision we have to only delete the bookmark and not strip
+            # anything. revsets cannot detect that case.
+            nodetobookmarks = {}
+            for mark, node in repomarks.iteritems():
+                nodetobookmarks.setdefault(node, []).append(mark)
+            for marks in nodetobookmarks.values():
+                if bookmarks.issuperset(marks):
+                    rsrevs = scmutil.bookmarkrevs(repo, marks[0])
+                    revs.update(set(rsrevs))
+            if not revs:
+                with repo.lock(), repo.transaction('bookmark') as tr:
+                    bmchanges = [(b, None) for b in bookmarks]
+                    repomarks.applychanges(repo, tr, bmchanges)
+                for bookmark in sorted(bookmarks):
+                    ui.write(_("bookmark '%s' deleted\n") % bookmark)
+
+        if not revs:
+            raise error.Abort(_('empty revision set'))
+
+        descendants = set(cl.descendants(revs))
+        strippedrevs = revs.union(descendants)
+        roots = revs.difference(descendants)
+
+        # if one of the wdir parent is stripped we'll need
+        # to update away to an earlier revision
+        update = any(p != nullid and cl.rev(p) in strippedrevs
+                     for p in repo.dirstate.parents())
+
+        rootnodes = set(cl.node(r) for r in roots)
+
+        q = getattr(repo, 'mq', None)
+        if q is not None and q.applied:
+            # refresh queue state if we're about to strip
+            # applied patches
+            if cl.rev(repo.lookup('qtip')) in strippedrevs:
+                q.applieddirty = True
+                start = 0
+                end = len(q.applied)
+                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.savedirty()
+
+        revs = sorted(rootnodes)
+        if update and opts.get('keep'):
+            urev = stripmod.findupdatetarget(repo, revs)
+            uctx = repo[urev]
+
+            # only reset the dirstate for files that would actually change
+            # between the working context and uctx
+            descendantrevs = repo.revs(b"%d::.", uctx.rev())
+            changedfiles = []
+            for rev in descendantrevs:
+                # blindly reset the files, regardless of what actually changed
+                changedfiles.extend(repo[rev].files())
+
+            # reset files that only changed in the dirstate too
+            dirstate = repo.dirstate
+            dirchanges = [f for f in dirstate if dirstate[f] != 'n']
+            changedfiles.extend(dirchanges)
+
+            repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
+            repo.dirstate.write(repo.currenttransaction())
+
+            # clear resolve state
+            mergemod.mergestate.clean(repo, repo['.'].node())
+
+            update = False
+
+
+        stripmod.strip(ui, repo, revs, backup=backup, update=update,
+                       force=opts.get('force'), bookmarks=bookmarks,
+                       soft=opts['soft'])
+
+    return 0
+
 @command('summary|sum',
     [('', 'remote', None, _('check for push and pull'))],
     '[--remote]',
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -94,6 +94,7 @@ 
     revsetlang,
     scmutil,
     smartset,
+    strip as stripmod,
     subrepoutil,
     util,
     vfs as vfsmod,
@@ -130,20 +131,7 @@ 
     default=False,
 )
 
-# force load strip extension formerly included in mq and import some utility
-try:
-    stripext = extensions.find('strip')
-except KeyError:
-    # note: load is lazy so we could avoid the try-except,
-    # but I (marmoute) prefer this explicit code.
-    class dummyui(object):
-        def debug(self, msg):
-            pass
-        def log(self, event, msgfmt, *msgargs, **opts):
-            pass
-    stripext = extensions.load(dummyui(), 'strip', '')
-
-strip = stripext.strip
+strip = stripmod.strip
 
 def checksubstate(repo, baserev=None):
     '''return list of subrepos at a different revision than substate.
diff --git a/contrib/win32/mercurial.ini b/contrib/win32/mercurial.ini
--- a/contrib/win32/mercurial.ini
+++ b/contrib/win32/mercurial.ini
@@ -64,6 +64,7 @@ 
 ;relink =
 ;schemes =
 ;share =
+;strip =

 ;transplant =
 ;win32mbcs =
 ;zeroconf =