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.
--- 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