split: take file patterns to limit selection on matching file patterns
authorBoris Feld <boris.feld@octobus.net>
Wed, 21 Feb 2018 14:39:48 +0100
changeset 4299 4af0235e7b0b
parent 4298 a98fa6c0d4f3
child 4300 702f7e1d0b01
split: take file patterns to limit selection on matching file patterns When splitting a big changeset, the user often wants to extract specific files from the changeset while leaving the others files intact. The split command can now be called with multiple file patterns. Only files that match one of the patterns will be shown in the interactive selection. The user will be able to create one or more commits with those files. When the user finishes splitting the matching files, a last commit will be made with the remaining of the files.
CHANGELOG
hgext3rd/evolve/cmdrewrite.py
tests/test-split.t
--- a/CHANGELOG	Wed Feb 21 12:39:17 2018 +0100
+++ b/CHANGELOG	Wed Feb 21 14:39:48 2018 +0100
@@ -7,6 +7,7 @@
   * split: improve and update the user prompt (BC)
   * split: make it possible to drop change during a split
   * split: no longer accept revision with --rev (BC)
+  * split: accept file patterns
   * push: have `--publish` overrule the `auto-publish` config
   * next: evolve aspiring children by default (use --no-evolve to skip)
   * next: pick lower part of a split as destination
--- a/hgext3rd/evolve/cmdrewrite.py	Wed Feb 21 12:39:17 2018 +0100
+++ b/hgext3rd/evolve/cmdrewrite.py	Wed Feb 21 14:39:48 2018 +0100
@@ -1135,15 +1135,19 @@
     [('r', 'rev', [], _("revision to split"), _('REV')),
      ('n', 'note', '', _("store a note on split"), _('TEXT')),
     ] + commitopts + commitopts2 + commitopts3,
-    _('hg split [OPTION]... [-r REV]'),
+    _('hg split [OPTION] [-r REV] [FILES]'),
     helpbasic=True)
-def cmdsplit(ui, repo, **opts):
+def cmdsplit(ui, repo, *pats, **opts):
     """split a changeset into smaller changesets
 
     By default, split the current revision by prompting for all its hunks to be
     redistributed into new changesets.
 
     Use --rev to split a given changeset instead.
+
+    If file patterns are specified only files matching these patterns will be
+    considered to be split in earlier changesets. The files that doesn't match
+    will be gathered in the last changeset.
     """
     _checknotesize(ui, opts)
     _resolveoptions(ui, opts)
@@ -1184,8 +1188,8 @@
         # Prepare the working directory
         rewriteutil.presplitupdate(repo, ui, prev, ctx)
 
-        def haschanges():
-            modified, added, removed, deleted = repo.status()[:4]
+        def haschanges(matcher=None):
+            modified, added, removed, deleted = repo.status(match=matcher)[:4]
             return modified or added or removed or deleted
         msg = ("HG: This is the original pre-split commit message. "
                "Edit it as appropriate.\n\n")
@@ -1199,14 +1203,27 @@
         # XXX-TODO: Find a way to set the branch without altering the dirstate
         repo.dirstate.setbranch(ctx.branch())
 
+        if pats:
+            # refresh the wctx used for the matcher
+            matcher = scmutil.match(repo[None], pats)
+        else:
+            matcher = scmutil.matchall(repo)
+
         while haschanges():
-            pats = ()
-            cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
-                             cmdutil.recordfilter, *pats, **opts)
-            # TODO: Does no seem like the best way to do this
-            # We should make dorecord return the newly created commit
-            newcommits.append(repo['.'])
-            if haschanges():
+
+            if haschanges(matcher):
+                cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
+                                 cmdutil.recordfilter, *pats, **opts)
+                # TODO: Does no seem like the best way to do this
+                # We should make dorecord return the newly created commit
+                newcommits.append(repo['.'])
+            if pats:
+                # refresh the wctx used for the matcher
+                matcher = scmutil.match(repo[None], pats)
+            else:
+                matcher = scmutil.matchall(repo)
+
+            if haschanges(matcher):
                 nextaction = None
                 while nextaction is None:
                     nextaction = ui.prompt('continue splitting? [Ycdq?]', default='y')
@@ -1223,8 +1240,17 @@
                         # prompting for confirmation
                         ui.status(_('discarding remaining changes\n'))
                         target = newcommits[0]
-                        cmdutil.revert(ui, repo, repo[target],
-                                       (target, node.nullid), all=True)
+                        if pats:
+                            status = repo.status(match=matcher)[:4]
+                            dirty = set()
+                            for i in status:
+                                dirty.update(i)
+                            dirty = sorted(dirty)
+                            cmdutil.revert(ui, repo, repo[target],
+                                           (target, node.nullid), *dirty)
+                        else:
+                            cmdutil.revert(ui, repo, repo[target],
+                                           (target, node.nullid), all=True)
                     elif nextaction == '?':
                         nextaction = None
                         ui.write(_("y - yes, continue selection\n"))
@@ -1237,7 +1263,11 @@
                 break # propagate the previous break
             else:
                 ui.status(_("no more change to split\n"))
-
+                if haschanges():
+                    # XXX: Should we show a message for informing the user
+                    # that we create another commit with remaining changes?
+                    commands.commit(ui, repo, **opts)
+                    newcommits.append(repo['.'])
         if newcommits:
             tip = repo[newcommits[-1]]
             bmupdate(tip.node())
--- a/tests/test-split.t	Wed Feb 21 12:39:17 2018 +0100
+++ b/tests/test-split.t	Wed Feb 21 14:39:48 2018 +0100
@@ -642,7 +642,6 @@
      summary:     split10
   
 
-
 Check prompt options
 --------------------
 
@@ -765,3 +764,136 @@
   ? SPLIT4
   ? editor.sh
   ? num
+
+Test restricting the split to a subset of files
+-----------------------------------------------
+
+  $ hg add SPLIT3 SPLIT4
+  $ hg amend
+
+Only run on 2 files
+
+(remaining changes gathered with unmatched one)
+
+  $ hg split SPLIT2 SPLIT3 << EOF
+  > y
+  > n
+  > c
+  > EOF
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  adding SPLIT2
+  adding SPLIT3
+  adding SPLIT4
+  diff --git a/SPLIT2 b/SPLIT2
+  new file mode 100644
+  examine changes to 'SPLIT2'? [Ynesfdaq?] y
+  
+  diff --git a/SPLIT3 b/SPLIT3
+  new file mode 100644
+  examine changes to 'SPLIT3'? [Ynesfdaq?] n
+  
+  continue splitting? [Ycdq?] c
+  $ hg status --change '.~1'
+  A SPLIT2
+  $ hg status --change '.'
+  A SPLIT3
+  A SPLIT4
+  $ hg fold --from '.~1'
+  2 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+(no remaining changes)
+
+  $ hg split SPLIT2 SPLIT3 << EOF
+  > y
+  > n
+  > y
+  > y
+  > EOF
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  adding SPLIT2
+  adding SPLIT3
+  adding SPLIT4
+  diff --git a/SPLIT2 b/SPLIT2
+  new file mode 100644
+  examine changes to 'SPLIT2'? [Ynesfdaq?] y
+  
+  diff --git a/SPLIT3 b/SPLIT3
+  new file mode 100644
+  examine changes to 'SPLIT3'? [Ynesfdaq?] n
+  
+  continue splitting? [Ycdq?] y
+  diff --git a/SPLIT3 b/SPLIT3
+  new file mode 100644
+  examine changes to 'SPLIT3'? [Ynesfdaq?] y
+  
+  no more change to split
+  $ hg status --change '.~2'
+  A SPLIT2
+  $ hg status --change '.~1'
+  A SPLIT3
+  $ hg status --change '.'
+  A SPLIT4
+  $ hg fold --from '.~2'
+  3 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+(only all matched selected)
+
+  $ hg split SPLIT2 SPLIT3 << EOF
+  > y
+  > y
+  > EOF
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  adding SPLIT2
+  adding SPLIT3
+  adding SPLIT4
+  diff --git a/SPLIT2 b/SPLIT2
+  new file mode 100644
+  examine changes to 'SPLIT2'? [Ynesfdaq?] y
+  
+  diff --git a/SPLIT3 b/SPLIT3
+  new file mode 100644
+  examine changes to 'SPLIT3'? [Ynesfdaq?] y
+  
+  no more change to split
+  $ hg status --change '.~1'
+  A SPLIT2
+  A SPLIT3
+  $ hg status --change '.'
+  A SPLIT4
+  $ hg fold --from '.~1'
+  2 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Check that discard does not alter unmatched files
+
+  $ hg split SPLIT2 SPLIT3 << EOF
+  > y
+  > n
+  > d
+  > EOF
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  adding SPLIT2
+  adding SPLIT3
+  adding SPLIT4
+  diff --git a/SPLIT2 b/SPLIT2
+  new file mode 100644
+  examine changes to 'SPLIT2'? [Ynesfdaq?] y
+  
+  diff --git a/SPLIT3 b/SPLIT3
+  new file mode 100644
+  examine changes to 'SPLIT3'? [Ynesfdaq?] n
+  
+  continue splitting? [Ycdq?] d
+  discarding remaining changes
+  no more change to split
+  $ hg status --change '.~1'
+  A SPLIT2
+  $ hg status --change '.'
+  A SPLIT4
+  $ hg fold --from '.~1'
+  2 changesets folded
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg add SPLIT3
+  $ hg amend