mq: add an '-e/--exact' option to qpush
This patch adds an '--exact/-e' option to qpush that will try to push the
patches in the correct location in the DAG. Specifying this option does the
following:
* If --move is specified, abort. It makes no sense to move a patch to the front
of the queue and try to apply it to its parent, because its parent is one of
the patches we just moved it in front of!
* If patches are already applied, abort. We don't want patch changesets
scattered throughout the DAG.
* If local changes are present, abort unless --force is used, as usual.
* Find the first patch we're going to push (if we're pushing multiple patches
with a target or --all).
* If that patch doesn't have a parent, abort, obviously.
* If the parent doesn't exist in the repo, abort. Something is wrong.
* Update to the parent, then continue pushing the patches as normal.
--- a/hgext/mq.py Mon Nov 22 12:43:31 2010 -0600
+++ b/hgext/mq.py Wed Nov 17 21:18:44 2010 -0500
@@ -1006,7 +1006,7 @@
raise util.Abort(_("patch %s not in series") % patch)
def push(self, repo, patch=None, force=False, list=False,
- mergeq=None, all=False, move=False):
+ mergeq=None, all=False, move=False, exact=False):
diffopts = self.diffopts()
wlock = repo.wlock()
try:
@@ -1015,7 +1015,7 @@
heads += ls
if not heads:
heads = [nullid]
- if repo.dirstate.parents()[0] not in heads:
+ if repo.dirstate.parents()[0] not in heads and not exact:
self.ui.status(_("(working directory not at a head)\n"))
if not self.series:
@@ -1062,6 +1062,18 @@
if not force:
self.check_localchanges(repo)
+ if exact:
+ if move:
+ raise util.Abort(_("cannot use --exact and --move together"))
+ if self.applied:
+ raise util.Abort(_("cannot push --exact with applied patches"))
+ root = self.series[start]
+ target = patchheader(self.join(root), self.plainmode).parent
+ if not target:
+ raise util.Abort(_("%s does not have a parent recorded" % root))
+ if not repo[target] == repo['.']:
+ hg.update(repo, target)
+
if move:
if not patch:
raise util.Abort(_("please specify the patch to move"))
@@ -2338,7 +2350,8 @@
mergeq = queue(ui, repo.join(""), newpath)
ui.warn(_("merging with queue at: %s\n") % mergeq.path)
ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
- mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
+ mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
+ exact=opts.get('exact'))
return ret
def pop(ui, repo, patch=None, **opts):
@@ -3114,6 +3127,7 @@
"^qpush":
(push,
[('f', 'force', None, _('apply on top of local changes')),
+ ('e', 'exact', None, _('apply the target patch to its recorded parent')),
('l', 'list', None, _('list patch name in commit text')),
('a', 'all', None, _('apply all patches')),
('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qpush-exact.t Wed Nov 17 21:18:44 2010 -0500
@@ -0,0 +1,290 @@
+ $ echo "[extensions]" >> $HGRCPATH
+ $ echo "mq=" >> $HGRCPATH
+ $ echo "graphlog=" >> $HGRCPATH
+
+make a test repository that looks like this:
+
+o 2:28bc7b1afd6a
+|
+| @ 1:d7fe2034f71b
+|/
+o 0/62ecad8b70e5
+
+ $ hg init r0
+ $ cd r0
+ $ touch f0
+ $ hg ci -m0 -Aq
+ $ touch f1
+ $ hg ci -m1 -Aq
+
+ $ hg update 0 -q
+ $ touch f2
+ $ hg ci -m2 -Aq
+ $ hg update 1 -q
+
+make some patches with a parent: 1:d7fe2034f71b -> p0 -> p1
+
+ $ echo cp0 >> fp0
+ $ hg add fp0
+ $ hg qnew p0 -d "0 0"
+
+ $ echo cp1 >> fp1
+ $ hg add fp1
+ $ hg qnew p1 -d "0 0"
+
+ $ hg qpop -aq
+ patch queue now empty
+
+qpush --exact when at the parent
+
+ $ hg update 1 -q
+ $ hg qpush -e
+ applying p0
+ now at: p0
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg qpush -e p0
+ applying p0
+ now at: p0
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg qpush -e p1
+ applying p0
+ applying p1
+ now at: p1
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg qpush -ea
+ applying p0
+ applying p1
+ now at: p1
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+qpush --exact when at another rev
+
+ $ hg update 0 -q
+ $ hg qpush -e
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ applying p0
+ now at: p0
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 0 -q
+ $ hg qpush -e p0
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ applying p0
+ now at: p0
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 0 -q
+ $ hg qpush -e p1
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ applying p0
+ applying p1
+ now at: p1
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 0 -q
+ $ hg qpush -ea
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ applying p0
+ applying p1
+ now at: p1
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+qpush --exact while crossing branches
+
+ $ hg update 2 -q
+ $ hg qpush -e
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ applying p0
+ now at: p0
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 2 -q
+ $ hg qpush -e p0
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ applying p0
+ now at: p0
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 2 -q
+ $ hg qpush -e p1
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ applying p0
+ applying p1
+ now at: p1
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 2 -q
+ $ hg qpush -ea
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ applying p0
+ applying p1
+ now at: p1
+ $ hg parents -qr qbase
+ 1:d7fe2034f71b
+ $ hg qpop -aq
+ patch queue now empty
+
+qpush --exact --force with changes to an unpatched file
+
+ $ hg update 1 -q
+ $ echo c0 >> f0
+ $ hg qpush -e
+ abort: local changes found, refresh first
+ [255]
+ $ hg qpush -ef
+ applying p0
+ now at: p0
+ $ cat f0
+ c0
+ $ rm f0
+ $ touch f0
+ $ hg qpop -aq
+ patch queue now empty
+
+ $ hg update 1 -q
+ $ echo c0 >> f0
+ $ hg qpush -e p1
+ abort: local changes found, refresh first
+ [255]
+ $ hg qpush -e p1 -f
+ applying p0
+ applying p1
+ now at: p1
+ $ cat f0
+ c0
+ $ rm f0
+ $ touch f0
+ $ hg qpop -aq
+ patch queue now empty
+
+qpush --exact --force with changes to a patched file
+
+ $ hg update 1 -q
+ $ echo cp0-bad >> fp0
+ $ hg add fp0
+ $ hg qpush -e
+ abort: local changes found, refresh first
+ [255]
+ $ hg qpush -ef
+ applying p0
+ file fp0 already exists
+ 1 out of 1 hunks FAILED -- saving rejects to file fp0.rej
+ patch failed, unable to continue (try -v)
+ patch failed, rejects left in working dir
+ errors during apply, please fix and refresh p0
+ [2]
+ $ cat fp0
+ cp0-bad
+ $ cat fp0.rej
+ --- fp0
+ +++ fp0
+ @@ -0,0 +1,1 @@
+ +cp0
+ $ hg qpop -aqf
+ patch queue now empty
+ $ rm fp0
+ $ rm fp0.rej
+
+ $ hg update 1 -q
+ $ echo cp1-bad >> fp1
+ $ hg add fp1
+ $ hg qpush -e p1
+ abort: local changes found, refresh first
+ [255]
+ $ hg qpush -e p1 -f
+ applying p0
+ applying p1
+ file fp1 already exists
+ 1 out of 1 hunks FAILED -- saving rejects to file fp1.rej
+ patch failed, unable to continue (try -v)
+ patch failed, rejects left in working dir
+ errors during apply, please fix and refresh p1
+ [2]
+ $ cat fp1
+ cp1-bad
+ $ cat fp1.rej
+ --- fp1
+ +++ fp1
+ @@ -0,0 +1,1 @@
+ +cp1
+ $ hg qpop -aqf
+ patch queue now empty
+ $ rm fp1
+ $ rm fp1.rej
+
+qpush --exact when already at a patch
+
+ $ hg update 1
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg qpush -e p0
+ applying p0
+ now at: p0
+ $ hg qpush -e p1
+ abort: cannot push --exact with applied patches
+ [255]
+ $ hg qpop -aq
+ patch queue now empty
+
+qpush --exact --move should fail
+
+ $ hg qpush -e --move p1
+ abort: cannot use --exact and --move together
+ [255]
+
+qpush --exact a patch without a parent recorded
+
+ $ hg qpush -q
+ now at: p0
+ $ grep -v '# Parent' .hg/patches/p0 > p0.new
+ $ mv p0.new .hg/patches/p0
+ $ hg qpop -aq
+ patch queue now empty
+ $ hg qpush -e
+ abort: p0 does not have a parent recorded
+ [255]
+ $ hg qpush -e p0
+ abort: p0 does not have a parent recorded
+ [255]
+ $ hg qpush -e p1
+ abort: p0 does not have a parent recorded
+ [255]
+ $ hg qpush -ea
+ abort: p0 does not have a parent recorded
+ [255]
+