2008-02-28 18:37:56 +03:00
|
|
|
# mq.py - patch queues for mercurial
|
2006-02-28 21:25:26 +03:00
|
|
|
#
|
2006-08-12 23:30:02 +04:00
|
|
|
# Copyright 2005, 2006 Chris Mason <mason@suse.com>
|
2006-02-28 21:25:26 +03:00
|
|
|
#
|
2009-04-26 03:08:54 +04:00
|
|
|
# This software may be used and distributed according to the terms of the
|
2010-01-20 07:20:08 +03:00
|
|
|
# GNU General Public License version 2 or any later version.
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2009-06-24 14:25:56 +04:00
|
|
|
'''manage a stack of patches
|
2006-07-04 01:30:38 +04:00
|
|
|
|
|
|
|
This extension lets you work with a stack of patches in a Mercurial
|
2009-07-26 03:53:49 +04:00
|
|
|
repository. It manages two stacks of patches - all known patches, and
|
|
|
|
applied patches (subset of known patches).
|
2006-07-04 01:30:38 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Known patches are represented as patch files in the .hg/patches
|
|
|
|
directory. Applied patches are both patch files and changesets.
|
2006-07-04 01:30:38 +04:00
|
|
|
|
2017-01-25 16:35:40 +03:00
|
|
|
Common tasks (use :hg:`help COMMAND` for more details)::
|
2006-07-04 01:30:38 +04:00
|
|
|
|
2009-07-17 01:25:26 +04:00
|
|
|
create new patch qnew
|
|
|
|
import existing patch qimport
|
2006-07-04 01:30:38 +04:00
|
|
|
|
2009-07-17 01:25:26 +04:00
|
|
|
print patch series qseries
|
|
|
|
print applied patches qapplied
|
2006-07-04 01:30:38 +04:00
|
|
|
|
2009-07-17 01:25:26 +04:00
|
|
|
add known patch to applied stack qpush
|
|
|
|
remove patch from applied stack qpop
|
|
|
|
refresh contents of top applied patch qrefresh
|
2010-01-01 23:21:34 +03:00
|
|
|
|
|
|
|
By default, mq will automatically use git patches when required to
|
|
|
|
avoid losing file mode changes, copy records, binary files or empty
|
2015-08-28 17:53:55 +03:00
|
|
|
files creations or deletions. This behavior can be configured with::
|
2010-01-01 23:21:34 +03:00
|
|
|
|
|
|
|
[mq]
|
|
|
|
git = auto/keep/yes/no
|
|
|
|
|
|
|
|
If set to 'keep', mq will obey the [diff] section configuration while
|
|
|
|
preserving existing git patches upon qrefresh. If set to 'yes' or
|
|
|
|
'no', mq will override the [diff] section and always generate git or
|
|
|
|
regular patches, possibly losing data in the second case.
|
2010-05-31 01:24:10 +04:00
|
|
|
|
2012-01-31 02:04:14 +04:00
|
|
|
It may be desirable for mq changesets to be kept in the secret phase (see
|
2012-01-29 22:59:21 +04:00
|
|
|
:hg:`help phases`), which can be enabled with the following setting::
|
|
|
|
|
|
|
|
[mq]
|
|
|
|
secret = True
|
|
|
|
|
2010-05-31 01:24:10 +04:00
|
|
|
You will by default be managing a patch queue named "patches". You can
|
|
|
|
create other, independent patch queues with the :hg:`qqueue` command.
|
2012-05-12 02:19:30 +04:00
|
|
|
|
|
|
|
If the working directory contains uncommitted files, qpush, qpop and
|
|
|
|
qgoto abort immediately. If -f/--force is used, the changes are
|
2012-05-13 16:00:58 +04:00
|
|
|
discarded. Setting::
|
2012-05-12 02:19:30 +04:00
|
|
|
|
|
|
|
[mq]
|
2012-05-13 16:00:58 +04:00
|
|
|
keepchanges = True
|
2012-05-12 02:19:30 +04:00
|
|
|
|
2012-05-13 16:00:58 +04:00
|
|
|
make them behave as if --keep-changes were passed, and non-conflicting
|
2012-05-12 02:19:30 +04:00
|
|
|
local changes will be tolerated and preserved. If incompatible options
|
|
|
|
such as -f/--force or --exact are passed, this setting is ignored.
|
2013-09-27 01:57:21 +04:00
|
|
|
|
|
|
|
This extension used to provide a strip command. This command now lives
|
|
|
|
in the strip extension.
|
2006-07-04 01:30:38 +04:00
|
|
|
'''
|
|
|
|
|
2016-05-06 19:24:31 +03:00
|
|
|
from __future__ import absolute_import
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2016-05-06 19:24:31 +03:00
|
|
|
import errno
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import shutil
|
|
|
|
from mercurial.i18n import _
|
|
|
|
from mercurial.node import (
|
|
|
|
bin,
|
|
|
|
hex,
|
|
|
|
nullid,
|
|
|
|
nullrev,
|
|
|
|
short,
|
|
|
|
)
|
|
|
|
from mercurial import (
|
|
|
|
cmdutil,
|
|
|
|
commands,
|
2016-11-22 05:05:52 +03:00
|
|
|
dirstateguard,
|
2016-05-06 19:24:31 +03:00
|
|
|
error,
|
|
|
|
extensions,
|
|
|
|
hg,
|
|
|
|
localrepo,
|
|
|
|
lock as lockmod,
|
|
|
|
patch as patchmod,
|
|
|
|
phases,
|
2016-11-22 21:33:11 +03:00
|
|
|
pycompat,
|
2016-05-06 19:24:31 +03:00
|
|
|
registrar,
|
2017-02-19 12:19:33 +03:00
|
|
|
revsetlang,
|
2016-05-06 19:24:31 +03:00
|
|
|
scmutil,
|
2017-02-19 12:16:09 +03:00
|
|
|
smartset,
|
2016-05-06 19:24:31 +03:00
|
|
|
subrepo,
|
|
|
|
util,
|
2017-03-02 15:31:23 +03:00
|
|
|
vfs as vfsmod,
|
2016-05-06 19:24:31 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
release = lockmod.release
|
2011-05-12 16:25:08 +04:00
|
|
|
seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
|
|
|
|
|
|
|
|
cmdtable = {}
|
2016-01-09 17:07:20 +03:00
|
|
|
command = registrar.command(cmdtable)
|
2016-08-23 18:26:08 +03:00
|
|
|
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
|
2015-04-28 23:44:37 +03:00
|
|
|
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
|
|
|
|
# be specifying the version(s) of Mercurial they are tested with, or
|
|
|
|
# leave the attribute unspecified.
|
2016-08-23 18:26:08 +03:00
|
|
|
testedwith = 'ships-with-hg-core'
|
2011-05-12 16:25:08 +04:00
|
|
|
|
2013-10-23 21:49:56 +04:00
|
|
|
# force load strip extension formerly included in mq and import some utility
|
2013-09-27 01:10:11 +04:00
|
|
|
try:
|
|
|
|
stripext = extensions.find('strip')
|
|
|
|
except KeyError:
|
|
|
|
# note: load is lazy so we could avoid the try-except,
|
2013-10-23 21:49:56 +04:00
|
|
|
# but I (marmoute) prefer this explicit code.
|
2013-09-27 01:10:11 +04:00
|
|
|
class dummyui(object):
|
|
|
|
def debug(self, msg):
|
|
|
|
pass
|
|
|
|
stripext = extensions.load(dummyui(), 'strip', '')
|
|
|
|
|
2013-09-27 01:43:00 +04:00
|
|
|
strip = stripext.strip
|
2013-09-27 01:12:43 +04:00
|
|
|
checksubstate = stripext.checksubstate
|
2013-09-27 01:32:52 +04:00
|
|
|
checklocalchanges = stripext.checklocalchanges
|
|
|
|
|
2013-09-27 01:12:43 +04:00
|
|
|
|
2007-01-18 00:19:36 +03:00
|
|
|
# Patch names looks like unix-file names.
|
|
|
|
# They must be joinable with queue directory and result in the patch path.
|
|
|
|
normname = util.normpath
|
|
|
|
|
2009-06-10 17:10:21 +04:00
|
|
|
class statusentry(object):
|
2010-03-14 03:35:54 +03:00
|
|
|
def __init__(self, node, name):
|
|
|
|
self.node, self.name = node, name
|
2010-06-17 17:53:26 +04:00
|
|
|
def __repr__(self):
|
2010-03-14 02:36:10 +03:00
|
|
|
return hex(self.node) + ':' + self.name
|
2006-08-03 22:09:12 +04:00
|
|
|
|
2014-09-24 04:41:11 +04:00
|
|
|
# The order of the headers in 'hg export' HG patches:
|
|
|
|
HGHEADERS = [
|
|
|
|
# '# HG changeset patch',
|
|
|
|
'# User ',
|
|
|
|
'# Date ',
|
|
|
|
'# ',
|
|
|
|
'# Branch ',
|
|
|
|
'# Node ID ',
|
|
|
|
'# Parent ', # can occur twice for merges - but that is not relevant for mq
|
|
|
|
]
|
2014-11-28 05:09:06 +03:00
|
|
|
# The order of headers in plain 'mail style' patches:
|
|
|
|
PLAINHEADERS = {
|
|
|
|
'from': 0,
|
|
|
|
'date': 1,
|
|
|
|
'subject': 2,
|
|
|
|
}
|
2014-09-24 04:41:11 +04:00
|
|
|
|
|
|
|
def inserthgheader(lines, header, value):
|
|
|
|
"""Assuming lines contains a HG patch header, add a header line with value.
|
|
|
|
>>> try: inserthgheader([], '# Date ', 'z')
|
|
|
|
... except ValueError, inst: print "oops"
|
|
|
|
oops
|
|
|
|
>>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
|
|
|
|
['# HG changeset patch', '# Date z']
|
|
|
|
>>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
|
|
|
|
['# HG changeset patch', '# Date z', '']
|
|
|
|
>>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
|
|
|
|
['# HG changeset patch', '# User y', '# Date z']
|
2014-11-28 04:57:33 +03:00
|
|
|
>>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
|
|
|
|
... '# User ', 'z')
|
|
|
|
['# HG changeset patch', '# Date x', '# User z']
|
2014-09-24 04:41:11 +04:00
|
|
|
>>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
|
|
|
|
['# HG changeset patch', '# Date z']
|
|
|
|
>>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
|
|
|
|
['# HG changeset patch', '# Date z', '', '# Date y']
|
|
|
|
>>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
|
|
|
|
['# HG changeset patch', '# Date z', '# Parent y']
|
|
|
|
"""
|
|
|
|
start = lines.index('# HG changeset patch') + 1
|
|
|
|
newindex = HGHEADERS.index(header)
|
2014-11-28 04:57:33 +03:00
|
|
|
bestpos = len(lines)
|
2014-09-24 04:41:11 +04:00
|
|
|
for i in range(start, len(lines)):
|
|
|
|
line = lines[i]
|
2014-11-28 04:57:33 +03:00
|
|
|
if not line.startswith('# '):
|
|
|
|
bestpos = min(bestpos, i)
|
|
|
|
break
|
2014-09-24 04:41:11 +04:00
|
|
|
for lineindex, h in enumerate(HGHEADERS):
|
|
|
|
if line.startswith(h):
|
|
|
|
if lineindex == newindex:
|
|
|
|
lines[i] = header + value
|
2014-11-28 04:57:33 +03:00
|
|
|
return lines
|
|
|
|
if lineindex > newindex:
|
|
|
|
bestpos = min(bestpos, i)
|
|
|
|
break # next line
|
|
|
|
lines.insert(bestpos, header + value)
|
2014-09-24 04:41:11 +04:00
|
|
|
return lines
|
|
|
|
|
2014-11-17 03:48:19 +03:00
|
|
|
def insertplainheader(lines, header, value):
|
2014-11-28 05:09:06 +03:00
|
|
|
"""For lines containing a plain patch header, add a header line with value.
|
|
|
|
>>> insertplainheader([], 'Date', 'z')
|
|
|
|
['Date: z']
|
|
|
|
>>> insertplainheader([''], 'Date', 'z')
|
|
|
|
['Date: z', '']
|
|
|
|
>>> insertplainheader(['x'], 'Date', 'z')
|
|
|
|
['Date: z', '', 'x']
|
|
|
|
>>> insertplainheader(['From: y', 'x'], 'Date', 'z')
|
|
|
|
['From: y', 'Date: z', '', 'x']
|
|
|
|
>>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
|
|
|
|
[' date : x', 'From: z', '']
|
|
|
|
>>> insertplainheader(['', 'Date: y'], 'Date', 'z')
|
|
|
|
['Date: z', '', 'Date: y']
|
|
|
|
>>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
|
|
|
|
['From: y', 'foo: bar', 'DATE: z', '', 'x']
|
|
|
|
"""
|
|
|
|
newprio = PLAINHEADERS[header.lower()]
|
|
|
|
bestpos = len(lines)
|
|
|
|
for i, line in enumerate(lines):
|
|
|
|
if ':' in line:
|
|
|
|
lheader = line.split(':', 1)[0].strip().lower()
|
|
|
|
lprio = PLAINHEADERS.get(lheader, newprio + 1)
|
|
|
|
if lprio == newprio:
|
|
|
|
lines[i] = '%s: %s' % (header, value)
|
|
|
|
return lines
|
|
|
|
if lprio > newprio and i < bestpos:
|
|
|
|
bestpos = i
|
|
|
|
else:
|
|
|
|
if line:
|
|
|
|
lines.insert(i, '')
|
|
|
|
if i < bestpos:
|
|
|
|
bestpos = i
|
|
|
|
break
|
|
|
|
lines.insert(bestpos, '%s: %s' % (header, value))
|
2014-11-17 03:48:19 +03:00
|
|
|
return lines
|
|
|
|
|
2008-11-23 01:04:42 +03:00
|
|
|
class patchheader(object):
|
2010-02-07 18:47:54 +03:00
|
|
|
def __init__(self, pf, plainmode=False):
|
2009-05-30 21:18:29 +04:00
|
|
|
def eatdiff(lines):
|
|
|
|
while lines:
|
|
|
|
l = lines[-1]
|
|
|
|
if (l.startswith("diff -") or
|
|
|
|
l.startswith("Index:") or
|
|
|
|
l.startswith("===========")):
|
|
|
|
del lines[-1]
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
def eatempty(lines):
|
|
|
|
while lines:
|
2010-03-14 21:09:55 +03:00
|
|
|
if not lines[-1].strip():
|
2009-05-30 21:18:29 +04:00
|
|
|
del lines[-1]
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
message = []
|
|
|
|
comments = []
|
|
|
|
user = None
|
|
|
|
date = None
|
2010-02-07 18:47:54 +03:00
|
|
|
parent = None
|
2009-05-30 21:18:29 +04:00
|
|
|
format = None
|
|
|
|
subject = None
|
2011-01-01 02:09:38 +03:00
|
|
|
branch = None
|
|
|
|
nodeid = None
|
2009-05-30 21:18:29 +04:00
|
|
|
diffstart = 0
|
|
|
|
|
|
|
|
for line in file(pf):
|
|
|
|
line = line.rstrip()
|
2010-03-18 21:26:56 +03:00
|
|
|
if (line.startswith('diff --git')
|
|
|
|
or (diffstart and line.startswith('+++ '))):
|
2009-05-30 21:18:29 +04:00
|
|
|
diffstart = 2
|
|
|
|
break
|
2010-03-18 21:26:56 +03:00
|
|
|
diffstart = 0 # reset
|
2009-05-30 21:18:29 +04:00
|
|
|
if line.startswith("--- "):
|
|
|
|
diffstart = 1
|
|
|
|
continue
|
|
|
|
elif format == "hgpatch":
|
|
|
|
# parse values when importing the result of an hg export
|
|
|
|
if line.startswith("# User "):
|
|
|
|
user = line[7:]
|
|
|
|
elif line.startswith("# Date "):
|
|
|
|
date = line[7:]
|
2010-02-07 18:47:54 +03:00
|
|
|
elif line.startswith("# Parent "):
|
2014-09-24 03:36:44 +04:00
|
|
|
parent = line[9:].lstrip() # handle double trailing space
|
2011-01-01 02:09:38 +03:00
|
|
|
elif line.startswith("# Branch "):
|
|
|
|
branch = line[9:]
|
|
|
|
elif line.startswith("# Node ID "):
|
|
|
|
nodeid = line[10:]
|
2009-05-30 21:18:29 +04:00
|
|
|
elif not line.startswith("# ") and line:
|
|
|
|
message.append(line)
|
|
|
|
format = None
|
|
|
|
elif line == '# HG changeset patch':
|
2009-07-28 17:36:28 +04:00
|
|
|
message = []
|
2009-05-30 21:18:29 +04:00
|
|
|
format = "hgpatch"
|
|
|
|
elif (format != "tagdone" and (line.startswith("Subject: ") or
|
|
|
|
line.startswith("subject: "))):
|
|
|
|
subject = line[9:]
|
|
|
|
format = "tag"
|
|
|
|
elif (format != "tagdone" and (line.startswith("From: ") or
|
|
|
|
line.startswith("from: "))):
|
|
|
|
user = line[6:]
|
|
|
|
format = "tag"
|
2010-02-07 18:47:54 +03:00
|
|
|
elif (format != "tagdone" and (line.startswith("Date: ") or
|
|
|
|
line.startswith("date: "))):
|
|
|
|
date = line[6:]
|
|
|
|
format = "tag"
|
2009-05-30 21:18:29 +04:00
|
|
|
elif format == "tag" and line == "":
|
|
|
|
# when looking for tags (subject: from: etc) they
|
|
|
|
# end once you find a blank line in the source
|
|
|
|
format = "tagdone"
|
|
|
|
elif message or line:
|
|
|
|
message.append(line)
|
|
|
|
comments.append(line)
|
|
|
|
|
|
|
|
eatdiff(message)
|
|
|
|
eatdiff(comments)
|
2011-01-01 02:09:38 +03:00
|
|
|
# Remember the exact starting line of the patch diffs before consuming
|
|
|
|
# empty lines, for external use by TortoiseHg and others
|
|
|
|
self.diffstartline = len(comments)
|
2009-05-30 21:18:29 +04:00
|
|
|
eatempty(message)
|
|
|
|
eatempty(comments)
|
|
|
|
|
|
|
|
# make sure message isn't empty
|
|
|
|
if format and format.startswith("tag") and subject:
|
|
|
|
message.insert(0, subject)
|
|
|
|
|
2008-11-23 01:04:42 +03:00
|
|
|
self.message = message
|
|
|
|
self.comments = comments
|
|
|
|
self.user = user
|
|
|
|
self.date = date
|
2010-02-07 18:47:54 +03:00
|
|
|
self.parent = parent
|
2011-01-01 02:09:38 +03:00
|
|
|
# nodeid and branch are for external use by TortoiseHg and others
|
|
|
|
self.nodeid = nodeid
|
|
|
|
self.branch = branch
|
2009-05-30 21:18:29 +04:00
|
|
|
self.haspatch = diffstart > 1
|
2014-09-20 19:06:56 +04:00
|
|
|
self.plainmode = (plainmode or
|
|
|
|
'# HG changeset patch' not in self.comments and
|
2015-05-16 21:30:07 +03:00
|
|
|
any(c.startswith('Date: ') or
|
2014-09-20 19:06:56 +04:00
|
|
|
c.startswith('From: ')
|
|
|
|
for c in self.comments))
|
2008-11-23 01:04:42 +03:00
|
|
|
|
|
|
|
def setuser(self, user):
|
2014-11-28 05:09:19 +03:00
|
|
|
try:
|
|
|
|
inserthgheader(self.comments, '# User ', user)
|
|
|
|
except ValueError:
|
|
|
|
if self.plainmode:
|
|
|
|
insertplainheader(self.comments, 'From', user)
|
|
|
|
else:
|
|
|
|
tmp = ['# HG changeset patch', '# User ' + user]
|
|
|
|
self.comments = tmp + self.comments
|
2008-11-23 01:04:42 +03:00
|
|
|
self.user = user
|
|
|
|
|
|
|
|
def setdate(self, date):
|
2014-11-28 05:09:19 +03:00
|
|
|
try:
|
|
|
|
inserthgheader(self.comments, '# Date ', date)
|
|
|
|
except ValueError:
|
|
|
|
if self.plainmode:
|
|
|
|
insertplainheader(self.comments, 'Date', date)
|
|
|
|
else:
|
|
|
|
tmp = ['# HG changeset patch', '# Date ' + date]
|
|
|
|
self.comments = tmp + self.comments
|
2009-08-12 12:57:27 +04:00
|
|
|
self.date = date
|
2008-11-23 01:04:42 +03:00
|
|
|
|
2010-02-07 18:47:54 +03:00
|
|
|
def setparent(self, parent):
|
2014-11-28 05:09:19 +03:00
|
|
|
try:
|
|
|
|
inserthgheader(self.comments, '# Parent ', parent)
|
|
|
|
except ValueError:
|
|
|
|
if not self.plainmode:
|
|
|
|
tmp = ['# HG changeset patch', '# Parent ' + parent]
|
|
|
|
self.comments = tmp + self.comments
|
2010-02-07 18:47:54 +03:00
|
|
|
self.parent = parent
|
|
|
|
|
2008-11-23 01:04:42 +03:00
|
|
|
def setmessage(self, message):
|
|
|
|
if self.comments:
|
|
|
|
self._delmsg()
|
|
|
|
self.message = [message]
|
2014-11-16 21:57:40 +03:00
|
|
|
if message:
|
|
|
|
if self.plainmode and self.comments and self.comments[-1]:
|
|
|
|
self.comments.append('')
|
|
|
|
self.comments.append(message)
|
2008-11-23 01:04:42 +03:00
|
|
|
|
|
|
|
def __str__(self):
|
2014-09-24 03:39:25 +04:00
|
|
|
s = '\n'.join(self.comments).rstrip()
|
|
|
|
if not s:
|
2008-11-23 01:04:42 +03:00
|
|
|
return ''
|
2014-09-24 03:39:25 +04:00
|
|
|
return s + '\n\n'
|
2008-11-23 01:04:42 +03:00
|
|
|
|
|
|
|
def _delmsg(self):
|
|
|
|
'''Remove existing message, keeping the rest of the comments fields.
|
|
|
|
If comments contains 'subject: ', message will prepend
|
|
|
|
the field and a blank line.'''
|
|
|
|
if self.message:
|
|
|
|
subj = 'subject: ' + self.message[0].lower()
|
|
|
|
for i in xrange(len(self.comments)):
|
|
|
|
if subj == self.comments[i].lower():
|
|
|
|
del self.comments[i]
|
|
|
|
self.message = self.message[2:]
|
|
|
|
break
|
|
|
|
ci = 0
|
2009-05-27 00:59:52 +04:00
|
|
|
for mi in self.message:
|
|
|
|
while mi != self.comments[ci]:
|
2008-11-23 01:04:42 +03:00
|
|
|
ci += 1
|
|
|
|
del self.comments[ci]
|
|
|
|
|
2012-02-10 23:47:57 +04:00
|
|
|
def newcommit(repo, phase, *args, **kwargs):
|
2012-01-30 03:05:28 +04:00
|
|
|
"""helper dedicated to ensure a commit respect mq.secret setting
|
2012-01-18 19:53:49 +04:00
|
|
|
|
2012-01-30 03:05:28 +04:00
|
|
|
It should be used instead of repo.commit inside the mq source for operation
|
|
|
|
creating new changeset.
|
2012-01-18 19:53:49 +04:00
|
|
|
"""
|
2012-10-08 20:02:25 +04:00
|
|
|
repo = repo.unfiltered()
|
2012-02-07 21:47:13 +04:00
|
|
|
if phase is None:
|
|
|
|
if repo.ui.configbool('mq', 'secret', False):
|
|
|
|
phase = phases.secret
|
2017-03-17 00:39:18 +03:00
|
|
|
overrides = {('ui', 'allowemptycommit'): True}
|
2012-02-07 21:47:13 +04:00
|
|
|
if phase is not None:
|
2017-03-17 00:39:18 +03:00
|
|
|
overrides[('phases', 'new-commit')] = phase
|
|
|
|
with repo.ui.configoverride(overrides, 'mq'):
|
2015-05-12 03:51:22 +03:00
|
|
|
repo.ui.setconfig('ui', 'allowemptycommit', True)
|
2012-01-18 19:53:49 +04:00
|
|
|
return repo.commit(*args, **kwargs)
|
|
|
|
|
2012-05-12 02:19:30 +04:00
|
|
|
class AbortNoCleanup(error.Abort):
|
|
|
|
pass
|
|
|
|
|
2009-06-10 17:10:21 +04:00
|
|
|
class queue(object):
|
2013-04-19 01:53:39 +04:00
|
|
|
def __init__(self, ui, baseui, path, patchdir=None):
|
2006-02-28 21:25:26 +03:00
|
|
|
self.basepath = path
|
2010-05-29 22:32:39 +04:00
|
|
|
try:
|
2010-06-02 21:39:45 +04:00
|
|
|
fh = open(os.path.join(path, 'patches.queue'))
|
|
|
|
cur = fh.read().rstrip()
|
2010-12-24 17:23:01 +03:00
|
|
|
fh.close()
|
2010-06-02 21:39:45 +04:00
|
|
|
if not cur:
|
|
|
|
curpath = os.path.join(path, 'patches')
|
|
|
|
else:
|
|
|
|
curpath = os.path.join(path, 'patches-' + cur)
|
2010-05-29 22:32:39 +04:00
|
|
|
except IOError:
|
|
|
|
curpath = os.path.join(path, 'patches')
|
|
|
|
self.path = patchdir or curpath
|
2017-03-02 15:31:23 +03:00
|
|
|
self.opener = vfsmod.vfs(self.path)
|
2006-02-28 21:25:26 +03:00
|
|
|
self.ui = ui
|
2013-04-19 01:53:39 +04:00
|
|
|
self.baseui = baseui
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = False
|
|
|
|
self.seriesdirty = False
|
2010-06-29 01:39:42 +04:00
|
|
|
self.added = []
|
2011-06-13 15:38:04 +04:00
|
|
|
self.seriespath = "series"
|
2011-06-13 15:39:14 +04:00
|
|
|
self.statuspath = "status"
|
2011-06-13 15:40:35 +04:00
|
|
|
self.guardspath = "guards"
|
2011-06-13 15:41:58 +04:00
|
|
|
self.activeguards = None
|
2011-06-13 15:43:03 +04:00
|
|
|
self.guardsdirty = False
|
2010-01-01 23:21:34 +03:00
|
|
|
# Handle mq.git as a bool with extended values
|
|
|
|
try:
|
|
|
|
gitmode = ui.configbool('mq', 'git', None)
|
|
|
|
if gitmode is None:
|
2012-05-12 18:00:58 +04:00
|
|
|
raise error.ConfigError
|
2015-03-14 00:00:06 +03:00
|
|
|
if gitmode:
|
|
|
|
self.gitmode = 'yes'
|
|
|
|
else:
|
|
|
|
self.gitmode = 'no'
|
2010-01-01 23:21:34 +03:00
|
|
|
except error.ConfigError:
|
2015-06-26 01:41:40 +03:00
|
|
|
# let's have check-config ignore the type mismatch
|
|
|
|
self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
|
|
|
|
# deprecated config: mq.plain
|
2010-02-07 18:47:54 +03:00
|
|
|
self.plainmode = ui.configbool('mq', 'plain', False)
|
2013-08-29 20:22:15 +04:00
|
|
|
self.checkapplied = True
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2009-05-04 05:35:42 +04:00
|
|
|
@util.propertycache
|
|
|
|
def applied(self):
|
2011-10-14 21:51:46 +04:00
|
|
|
def parselines(lines):
|
|
|
|
for l in lines:
|
|
|
|
entry = l.split(':', 1)
|
|
|
|
if len(entry) > 1:
|
|
|
|
n, name = entry
|
|
|
|
yield statusentry(bin(n), name)
|
|
|
|
elif l.strip():
|
|
|
|
self.ui.warn(_('malformated mq status line: %s\n') % entry)
|
|
|
|
# else we ignore empty lines
|
|
|
|
try:
|
2011-06-13 15:39:14 +04:00
|
|
|
lines = self.opener.read(self.statuspath).splitlines()
|
2011-02-11 15:10:39 +03:00
|
|
|
return list(parselines(lines))
|
2015-06-24 08:20:08 +03:00
|
|
|
except IOError as e:
|
2011-10-14 21:51:46 +04:00
|
|
|
if e.errno == errno.ENOENT:
|
|
|
|
return []
|
|
|
|
raise
|
2009-05-04 05:35:42 +04:00
|
|
|
|
|
|
|
@util.propertycache
|
2011-06-13 13:06:11 +04:00
|
|
|
def fullseries(self):
|
2011-10-14 21:51:46 +04:00
|
|
|
try:
|
2012-01-11 05:28:12 +04:00
|
|
|
return self.opener.read(self.seriespath).splitlines()
|
2015-06-24 08:20:08 +03:00
|
|
|
except IOError as e:
|
2011-10-14 21:51:46 +04:00
|
|
|
if e.errno == errno.ENOENT:
|
|
|
|
return []
|
|
|
|
raise
|
2009-05-04 05:35:42 +04:00
|
|
|
|
|
|
|
@util.propertycache
|
|
|
|
def series(self):
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2009-05-04 05:35:42 +04:00
|
|
|
return self.series
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2009-05-04 05:35:42 +04:00
|
|
|
@util.propertycache
|
2011-06-13 13:40:27 +04:00
|
|
|
def seriesguards(self):
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2011-06-13 13:40:27 +04:00
|
|
|
return self.seriesguards
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2009-05-05 12:29:34 +04:00
|
|
|
def invalidate(self):
|
2011-06-13 13:40:27 +04:00
|
|
|
for a in 'applied fullseries series seriesguards'.split():
|
2009-05-05 12:29:34 +04:00
|
|
|
if a in self.__dict__:
|
|
|
|
delattr(self, a)
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = False
|
|
|
|
self.seriesdirty = False
|
2011-06-13 15:43:03 +04:00
|
|
|
self.guardsdirty = False
|
2011-06-13 15:41:58 +04:00
|
|
|
self.activeguards = None
|
2009-05-05 12:29:34 +04:00
|
|
|
|
2015-09-24 10:50:06 +03:00
|
|
|
def diffopts(self, opts=None, patchfn=None):
|
2011-05-06 20:03:45 +04:00
|
|
|
diffopts = patchmod.diffopts(self.ui, opts)
|
2010-01-01 23:21:34 +03:00
|
|
|
if self.gitmode == 'auto':
|
|
|
|
diffopts.upgrade = True
|
|
|
|
elif self.gitmode == 'keep':
|
|
|
|
pass
|
|
|
|
elif self.gitmode in ('yes', 'no'):
|
|
|
|
diffopts.git = self.gitmode == 'yes'
|
|
|
|
else:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('mq.git option can be auto/keep/yes/no'
|
2010-01-01 23:21:34 +03:00
|
|
|
' got %s') % self.gitmode)
|
2010-01-01 21:53:05 +03:00
|
|
|
if patchfn:
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = self.patchopts(diffopts, patchfn)
|
|
|
|
return diffopts
|
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
def patchopts(self, diffopts, *patches):
|
2010-01-01 21:53:05 +03:00
|
|
|
"""Return a copy of input diff options with git set to true if
|
2010-01-01 23:21:34 +03:00
|
|
|
referenced patch is a git patch and should be preserved as such.
|
2010-01-01 21:53:05 +03:00
|
|
|
"""
|
|
|
|
diffopts = diffopts.copy()
|
2010-01-01 23:21:34 +03:00
|
|
|
if not diffopts.git and self.gitmode == 'keep':
|
|
|
|
for patchfn in patches:
|
|
|
|
patchf = self.opener(patchfn, 'r')
|
|
|
|
# if the patch was a git patch, refresh it as a git patch
|
|
|
|
for line in patchf:
|
|
|
|
if line.startswith('diff --git'):
|
|
|
|
diffopts.git = True
|
|
|
|
break
|
|
|
|
patchf.close()
|
2010-01-01 21:53:05 +03:00
|
|
|
return diffopts
|
2006-08-13 03:13:27 +04:00
|
|
|
|
2006-08-09 05:12:48 +04:00
|
|
|
def join(self, *p):
|
|
|
|
return os.path.join(self.path, *p)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-06-13 13:54:25 +04:00
|
|
|
def findseries(self, patch):
|
2010-03-14 14:08:14 +03:00
|
|
|
def matchpatch(l):
|
|
|
|
l = l.split('#', 1)[0]
|
|
|
|
return l.strip() == patch
|
2011-06-13 13:06:11 +04:00
|
|
|
for index, l in enumerate(self.fullseries):
|
2010-03-14 14:08:14 +03:00
|
|
|
if matchpatch(l):
|
|
|
|
return index
|
2006-02-28 21:25:26 +03:00
|
|
|
return None
|
|
|
|
|
2006-08-09 08:42:50 +04:00
|
|
|
guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
|
|
|
|
|
2011-06-13 13:55:46 +04:00
|
|
|
def parseseries(self):
|
2006-02-28 21:25:26 +03:00
|
|
|
self.series = []
|
2011-06-13 13:40:27 +04:00
|
|
|
self.seriesguards = []
|
2011-06-13 13:06:11 +04:00
|
|
|
for l in self.fullseries:
|
2006-08-09 08:42:50 +04:00
|
|
|
h = l.find('#')
|
|
|
|
if h == -1:
|
|
|
|
patch = l
|
|
|
|
comment = ''
|
|
|
|
elif h == 0:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
patch = l[:h]
|
|
|
|
comment = l[h:]
|
|
|
|
patch = patch.strip()
|
|
|
|
if patch:
|
2006-09-28 01:32:53 +04:00
|
|
|
if patch in self.series:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('%s appears more than once in %s') %
|
2011-06-13 15:38:04 +04:00
|
|
|
(patch, self.join(self.seriespath)))
|
2006-08-09 08:42:50 +04:00
|
|
|
self.series.append(patch)
|
2011-06-13 13:40:27 +04:00
|
|
|
self.seriesguards.append(self.guard_re.findall(comment))
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2011-06-13 13:57:21 +04:00
|
|
|
def checkguard(self, guard):
|
2008-05-18 19:01:24 +04:00
|
|
|
if not guard:
|
|
|
|
return _('guard cannot be an empty string')
|
2006-08-09 08:42:50 +04:00
|
|
|
bad_chars = '# \t\r\n\f'
|
|
|
|
first = guard[0]
|
2009-05-04 05:49:57 +04:00
|
|
|
if first in '-+':
|
|
|
|
return (_('guard %r starts with invalid character: %r') %
|
|
|
|
(guard, first))
|
2006-08-09 08:42:50 +04:00
|
|
|
for c in bad_chars:
|
|
|
|
if c in guard:
|
|
|
|
return _('invalid character in guard %r: %r') % (guard, c)
|
2006-10-01 21:26:33 +04:00
|
|
|
|
2011-06-13 13:59:35 +04:00
|
|
|
def setactive(self, guards):
|
2006-08-09 08:42:50 +04:00
|
|
|
for guard in guards:
|
2011-06-13 13:57:21 +04:00
|
|
|
bad = self.checkguard(guard)
|
2006-08-09 08:42:50 +04:00
|
|
|
if bad:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(bad)
|
2009-04-27 01:50:44 +04:00
|
|
|
guards = sorted(set(guards))
|
2009-09-19 03:15:38 +04:00
|
|
|
self.ui.debug('active guards: %s\n' % ' '.join(guards))
|
2011-06-13 15:41:58 +04:00
|
|
|
self.activeguards = guards
|
2011-06-13 15:43:03 +04:00
|
|
|
self.guardsdirty = True
|
2006-08-09 08:42:50 +04:00
|
|
|
|
|
|
|
def active(self):
|
2011-06-13 15:41:58 +04:00
|
|
|
if self.activeguards is None:
|
|
|
|
self.activeguards = []
|
2006-08-09 08:42:50 +04:00
|
|
|
try:
|
2011-06-13 15:40:35 +04:00
|
|
|
guards = self.opener.read(self.guardspath).split()
|
2015-06-24 08:20:08 +03:00
|
|
|
except IOError as err:
|
2010-01-25 09:05:27 +03:00
|
|
|
if err.errno != errno.ENOENT:
|
|
|
|
raise
|
2006-08-09 08:42:50 +04:00
|
|
|
guards = []
|
|
|
|
for i, guard in enumerate(guards):
|
2011-06-13 13:57:21 +04:00
|
|
|
bad = self.checkguard(guard)
|
2006-08-09 08:42:50 +04:00
|
|
|
if bad:
|
|
|
|
self.ui.warn('%s:%d: %s\n' %
|
2011-06-13 15:40:35 +04:00
|
|
|
(self.join(self.guardspath), i + 1, bad))
|
2006-08-09 08:42:50 +04:00
|
|
|
else:
|
2011-06-13 15:41:58 +04:00
|
|
|
self.activeguards.append(guard)
|
|
|
|
return self.activeguards
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2011-06-13 13:58:16 +04:00
|
|
|
def setguards(self, idx, guards):
|
2006-08-09 08:42:50 +04:00
|
|
|
for g in guards:
|
|
|
|
if len(g) < 2:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('guard %r too short') % g)
|
2006-08-09 08:42:50 +04:00
|
|
|
if g[0] not in '-+':
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('guard %r starts with invalid char') % g)
|
2011-06-13 13:57:21 +04:00
|
|
|
bad = self.checkguard(g[1:])
|
2006-08-09 08:42:50 +04:00
|
|
|
if bad:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(bad)
|
2011-06-13 13:06:11 +04:00
|
|
|
drop = self.guard_re.sub('', self.fullseries[idx])
|
|
|
|
self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2011-06-13 15:45:48 +04:00
|
|
|
self.seriesdirty = True
|
2006-10-01 21:26:33 +04:00
|
|
|
|
2006-08-09 08:42:50 +04:00
|
|
|
def pushable(self, idx):
|
|
|
|
if isinstance(idx, str):
|
|
|
|
idx = self.series.index(idx)
|
2011-06-13 13:40:27 +04:00
|
|
|
patchguards = self.seriesguards[idx]
|
2006-08-09 08:42:50 +04:00
|
|
|
if not patchguards:
|
|
|
|
return True, None
|
|
|
|
guards = self.active()
|
|
|
|
exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
|
|
|
|
if exactneg:
|
2011-05-31 10:47:16 +04:00
|
|
|
return False, repr(exactneg[0])
|
2006-08-09 08:42:50 +04:00
|
|
|
pos = [g for g in patchguards if g[0] == '+']
|
2006-08-11 04:18:55 +04:00
|
|
|
exactpos = [g for g in pos if g[1:] in guards]
|
2006-08-09 08:42:50 +04:00
|
|
|
if pos:
|
2006-08-11 04:18:55 +04:00
|
|
|
if exactpos:
|
2011-05-31 10:47:16 +04:00
|
|
|
return True, repr(exactpos[0])
|
|
|
|
return False, ' '.join(map(repr, pos))
|
2006-08-09 08:42:50 +04:00
|
|
|
return True, ''
|
|
|
|
|
2011-06-13 14:00:56 +04:00
|
|
|
def explainpushable(self, idx, all_patches=False):
|
2015-03-14 00:00:06 +03:00
|
|
|
if all_patches:
|
|
|
|
write = self.ui.write
|
|
|
|
else:
|
|
|
|
write = self.ui.warn
|
|
|
|
|
2006-08-09 08:42:50 +04:00
|
|
|
if all_patches or self.ui.verbose:
|
|
|
|
if isinstance(idx, str):
|
|
|
|
idx = self.series.index(idx)
|
|
|
|
pushable, why = self.pushable(idx)
|
|
|
|
if all_patches and pushable:
|
|
|
|
if why is None:
|
|
|
|
write(_('allowing %s - no guards in effect\n') %
|
|
|
|
self.series[idx])
|
|
|
|
else:
|
|
|
|
if not why:
|
|
|
|
write(_('allowing %s - no matching negative guards\n') %
|
|
|
|
self.series[idx])
|
|
|
|
else:
|
2011-05-31 10:47:16 +04:00
|
|
|
write(_('allowing %s - guarded by %s\n') %
|
2006-08-09 08:42:50 +04:00
|
|
|
(self.series[idx], why))
|
|
|
|
if not pushable:
|
2006-08-09 20:38:11 +04:00
|
|
|
if why:
|
2011-05-31 10:47:16 +04:00
|
|
|
write(_('skipping %s - guarded by %s\n') %
|
2006-12-12 03:32:45 +03:00
|
|
|
(self.series[idx], why))
|
2006-08-09 08:42:50 +04:00
|
|
|
else:
|
|
|
|
write(_('skipping %s - no matching guards\n') %
|
|
|
|
self.series[idx])
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2011-06-13 14:02:14 +04:00
|
|
|
def savedirty(self):
|
2011-06-13 15:47:21 +04:00
|
|
|
def writelist(items, path):
|
2006-08-03 07:33:57 +04:00
|
|
|
fp = self.opener(path, 'w')
|
|
|
|
for i in items:
|
2008-01-18 19:48:25 +03:00
|
|
|
fp.write("%s\n" % i)
|
2006-08-03 07:33:57 +04:00
|
|
|
fp.close()
|
2011-06-13 15:44:34 +04:00
|
|
|
if self.applieddirty:
|
2011-06-13 15:47:21 +04:00
|
|
|
writelist(map(str, self.applied), self.statuspath)
|
2012-01-11 05:29:56 +04:00
|
|
|
self.applieddirty = False
|
2011-06-13 15:45:48 +04:00
|
|
|
if self.seriesdirty:
|
2011-06-13 15:47:21 +04:00
|
|
|
writelist(self.fullseries, self.seriespath)
|
2012-01-11 05:29:56 +04:00
|
|
|
self.seriesdirty = False
|
2011-06-13 15:43:03 +04:00
|
|
|
if self.guardsdirty:
|
2011-06-13 15:47:21 +04:00
|
|
|
writelist(self.activeguards, self.guardspath)
|
2012-01-11 05:29:56 +04:00
|
|
|
self.guardsdirty = False
|
2010-07-13 17:30:01 +04:00
|
|
|
if self.added:
|
|
|
|
qrepo = self.qrepo()
|
|
|
|
if qrepo:
|
2010-10-09 20:53:48 +04:00
|
|
|
qrepo[None].add(f for f in self.added if f not in qrepo[None])
|
2010-07-13 17:30:01 +04:00
|
|
|
self.added = []
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2007-03-14 07:50:42 +03:00
|
|
|
def removeundo(self, repo):
|
|
|
|
undo = repo.sjoin('undo')
|
|
|
|
if not os.path.exists(undo):
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
os.unlink(undo)
|
2015-06-24 08:20:08 +03:00
|
|
|
except OSError as inst:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_('error removing undo: %s\n') % str(inst))
|
2007-03-14 07:50:42 +03:00
|
|
|
|
2012-05-11 18:18:47 +04:00
|
|
|
def backup(self, repo, files, copy=False):
|
2012-05-11 18:17:02 +04:00
|
|
|
# backup local changes in --force case
|
|
|
|
for f in sorted(files):
|
|
|
|
absf = repo.wjoin(f)
|
|
|
|
if os.path.lexists(absf):
|
|
|
|
self.ui.note(_('saving current version of %s as %s\n') %
|
2016-01-02 14:02:57 +03:00
|
|
|
(f, scmutil.origpath(self.ui, repo, f)))
|
2015-11-11 01:52:54 +03:00
|
|
|
|
2016-01-02 14:02:57 +03:00
|
|
|
absorig = scmutil.origpath(self.ui, repo, absf)
|
2012-05-11 18:18:47 +04:00
|
|
|
if copy:
|
2015-11-11 01:52:54 +03:00
|
|
|
util.copyfile(absf, absorig)
|
2012-05-11 18:18:47 +04:00
|
|
|
else:
|
2015-11-11 01:52:54 +03:00
|
|
|
util.rename(absf, absorig)
|
2012-05-11 18:17:02 +04:00
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
def printdiff(self, repo, diffopts, node1, node2=None, files=None,
|
2017-03-13 07:55:46 +03:00
|
|
|
fp=None, changes=None, opts=None):
|
2017-03-16 01:05:54 +03:00
|
|
|
if opts is None:
|
|
|
|
opts = {}
|
2009-10-25 03:52:35 +03:00
|
|
|
stat = opts.get('stat')
|
2011-06-19 01:52:51 +04:00
|
|
|
m = scmutil.match(repo[node1], files, opts)
|
2010-04-06 19:45:20 +04:00
|
|
|
cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
|
|
|
|
changes, stat, fp)
|
2006-08-13 03:13:27 +04:00
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
|
2006-02-28 21:25:26 +03:00
|
|
|
# first try just applying the patch
|
2010-01-25 09:05:27 +03:00
|
|
|
(err, n) = self.apply(repo, [patch], update_status=False,
|
2007-07-22 01:02:10 +04:00
|
|
|
strict=True, merge=rev)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
if err == 0:
|
|
|
|
return (err, n)
|
|
|
|
|
|
|
|
if n is None:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("apply failed for patch %s") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
# apply failed, strip away that rev and merge.
|
2007-07-22 01:02:10 +04:00
|
|
|
hg.clean(repo, head)
|
2014-07-24 23:06:08 +04:00
|
|
|
strip(self.ui, repo, [n], update=False, backup=False)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2008-06-26 23:35:46 +04:00
|
|
|
ctx = repo[rev]
|
2007-07-22 01:02:10 +04:00
|
|
|
ret = hg.merge(repo, rev)
|
2006-02-28 21:25:26 +03:00
|
|
|
if ret:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("update returned %d") % ret)
|
2012-02-10 23:47:57 +04:00
|
|
|
n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
|
2009-05-20 02:52:46 +04:00
|
|
|
if n is None:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("repo commit failed"))
|
2006-02-28 21:25:26 +03:00
|
|
|
try:
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(mergeq.join(patch), self.plainmode)
|
2012-05-12 18:02:46 +04:00
|
|
|
except Exception:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("unable to read %s") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = self.patchopts(diffopts, patch)
|
2006-03-08 00:41:24 +03:00
|
|
|
patchf = self.opener(patch, "w")
|
2008-11-23 01:04:42 +03:00
|
|
|
comments = str(ph)
|
2006-02-28 21:25:26 +03:00
|
|
|
if comments:
|
|
|
|
patchf.write(comments)
|
2010-01-01 21:53:05 +03:00
|
|
|
self.printdiff(repo, diffopts, head, n, fp=patchf)
|
2006-02-28 21:25:26 +03:00
|
|
|
patchf.close()
|
2007-03-14 07:50:42 +03:00
|
|
|
self.removeundo(repo)
|
2006-02-28 21:25:26 +03:00
|
|
|
return (0, n)
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
def qparents(self, repo, rev=None):
|
2013-09-25 21:34:45 +04:00
|
|
|
"""return the mq handled parent or p1
|
|
|
|
|
|
|
|
In some case where mq get himself in being the parent of a merge the
|
2013-10-23 21:49:56 +04:00
|
|
|
appropriate parent may be p2.
|
2013-09-25 21:34:45 +04:00
|
|
|
(eg: an in progress merge started with mq disabled)
|
|
|
|
|
|
|
|
If no parent are managed by mq, p1 is returned.
|
|
|
|
"""
|
2006-02-28 21:25:26 +03:00
|
|
|
if rev is None:
|
|
|
|
(p1, p2) = repo.dirstate.parents()
|
2009-01-12 19:59:08 +03:00
|
|
|
if p2 == nullid:
|
2006-02-28 21:25:26 +03:00
|
|
|
return p1
|
2010-03-14 16:45:01 +03:00
|
|
|
if not self.applied:
|
2006-02-28 21:25:26 +03:00
|
|
|
return None
|
2010-03-14 02:36:10 +03:00
|
|
|
return self.applied[-1].node
|
|
|
|
p1, p2 = repo.changelog.parents(rev)
|
2010-03-14 03:01:17 +03:00
|
|
|
if p2 != nullid and p2 in [x.node for x in self.applied]:
|
|
|
|
return p2
|
2010-03-14 02:36:10 +03:00
|
|
|
return p1
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
def mergepatch(self, repo, mergeq, series, diffopts):
|
2010-03-14 16:45:01 +03:00
|
|
|
if not self.applied:
|
2006-02-28 21:25:26 +03:00
|
|
|
# each of the patches merged in will have two parents. This
|
|
|
|
# can confuse the qrefresh, qdiff, and strip code because it
|
|
|
|
# needs to know which parent is actually in the patch queue.
|
|
|
|
# so, we insert a merge marker with only one parent. This way
|
|
|
|
# the first patch in the queue is never a merge patch
|
|
|
|
#
|
|
|
|
pname = ".hg.patches.merge.marker"
|
2012-02-10 23:47:57 +04:00
|
|
|
n = newcommit(repo, None, '[mq]: merge marker', force=True)
|
2007-03-14 07:50:42 +03:00
|
|
|
self.removeundo(repo)
|
2010-03-14 02:36:10 +03:00
|
|
|
self.applied.append(statusentry(n, pname))
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
head = self.qparents(repo)
|
|
|
|
|
|
|
|
for patch in series:
|
2006-07-27 20:27:35 +04:00
|
|
|
patch = mergeq.lookup(patch, strict=True)
|
2006-02-28 21:25:26 +03:00
|
|
|
if not patch:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("patch %s does not exist\n") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
return (1, None)
|
2006-08-09 08:42:50 +04:00
|
|
|
pushable, reason = self.pushable(patch)
|
|
|
|
if not pushable:
|
2011-06-13 14:00:56 +04:00
|
|
|
self.explainpushable(patch, all_patches=True)
|
2006-08-09 08:42:50 +04:00
|
|
|
continue
|
2006-02-28 21:25:26 +03:00
|
|
|
info = mergeq.isapplied(patch)
|
|
|
|
if not info:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("patch %s is not applied\n") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
return (1, None)
|
2010-03-14 02:36:10 +03:00
|
|
|
rev = info[1]
|
2010-01-01 21:53:05 +03:00
|
|
|
err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
|
2006-02-28 21:25:26 +03:00
|
|
|
if head:
|
2010-03-14 02:36:10 +03:00
|
|
|
self.applied.append(statusentry(head, patch))
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2006-02-28 21:25:26 +03:00
|
|
|
if err:
|
|
|
|
return (err, head)
|
2011-06-13 14:02:14 +04:00
|
|
|
self.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
return (0, head)
|
|
|
|
|
2006-08-01 07:33:56 +04:00
|
|
|
def patch(self, repo, patchfile):
|
|
|
|
'''Apply patchfile to the working directory.
|
2009-06-09 17:25:17 +04:00
|
|
|
patchfile: name of patch file'''
|
2011-06-11 16:14:11 +04:00
|
|
|
files = set()
|
2006-08-01 07:33:56 +04:00
|
|
|
try:
|
2011-05-08 19:48:31 +04:00
|
|
|
fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
|
2011-05-20 00:44:01 +04:00
|
|
|
files=files, eolmode=None)
|
2011-05-08 19:48:31 +04:00
|
|
|
return (True, list(files), fuzz)
|
2015-06-24 08:20:08 +03:00
|
|
|
except Exception as inst:
|
2006-08-15 22:28:50 +04:00
|
|
|
self.ui.note(str(inst) + '\n')
|
|
|
|
if not self.ui.verbose:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
|
2011-09-11 20:49:38 +04:00
|
|
|
self.ui.traceback()
|
2011-05-08 19:48:31 +04:00
|
|
|
return (False, list(files), False)
|
2006-08-15 22:28:50 +04:00
|
|
|
|
2006-03-01 01:25:34 +03:00
|
|
|
def apply(self, repo, series, list=False, update_status=True,
|
2012-05-11 18:18:47 +04:00
|
|
|
strict=False, patchdir=None, merge=None, all_files=None,
|
2012-05-13 16:00:58 +04:00
|
|
|
tobackup=None, keepchanges=False):
|
2015-10-08 21:53:46 +03:00
|
|
|
wlock = lock = tr = None
|
2007-05-08 23:16:02 +04:00
|
|
|
try:
|
2007-07-22 01:02:10 +04:00
|
|
|
wlock = repo.wlock()
|
2007-07-22 01:02:10 +04:00
|
|
|
lock = repo.lock()
|
2010-04-10 02:23:35 +04:00
|
|
|
tr = repo.transaction("qpush")
|
2007-05-08 23:16:02 +04:00
|
|
|
try:
|
2007-07-22 23:53:57 +04:00
|
|
|
ret = self._apply(repo, series, list, update_status,
|
2012-05-11 18:18:47 +04:00
|
|
|
strict, patchdir, merge, all_files=all_files,
|
2012-05-13 16:00:58 +04:00
|
|
|
tobackup=tobackup, keepchanges=keepchanges)
|
2007-07-22 01:02:10 +04:00
|
|
|
tr.close()
|
2011-06-13 14:02:14 +04:00
|
|
|
self.savedirty()
|
2007-07-22 01:02:10 +04:00
|
|
|
return ret
|
2012-05-12 02:19:30 +04:00
|
|
|
except AbortNoCleanup:
|
|
|
|
tr.close()
|
|
|
|
self.savedirty()
|
2015-04-21 02:13:44 +03:00
|
|
|
raise
|
2012-05-13 15:18:06 +04:00
|
|
|
except: # re-raises
|
2007-07-22 01:02:10 +04:00
|
|
|
try:
|
|
|
|
tr.abort()
|
|
|
|
finally:
|
2012-01-11 05:29:55 +04:00
|
|
|
self.invalidate()
|
2007-07-22 01:02:10 +04:00
|
|
|
raise
|
|
|
|
finally:
|
2015-10-08 21:53:46 +03:00
|
|
|
release(tr, lock, wlock)
|
2007-11-10 01:21:35 +03:00
|
|
|
self.removeundo(repo)
|
2007-05-08 23:16:02 +04:00
|
|
|
|
2007-07-22 23:53:57 +04:00
|
|
|
def _apply(self, repo, series, list=False, update_status=True,
|
2012-05-11 18:18:47 +04:00
|
|
|
strict=False, patchdir=None, merge=None, all_files=None,
|
2012-05-13 16:00:58 +04:00
|
|
|
tobackup=None, keepchanges=False):
|
2012-05-11 18:18:47 +04:00
|
|
|
"""returns (error, hash)
|
|
|
|
|
|
|
|
error = 1 for unable to read, 2 for patch failed, 3 for patch
|
|
|
|
fuzz. tobackup is None or a set of files to backup before they
|
|
|
|
are modified by a patch.
|
|
|
|
"""
|
2006-02-28 21:25:26 +03:00
|
|
|
# TODO unify with commands.py
|
|
|
|
if not patchdir:
|
|
|
|
patchdir = self.path
|
|
|
|
err = 0
|
|
|
|
n = None
|
2006-08-17 06:49:45 +04:00
|
|
|
for patchname in series:
|
|
|
|
pushable, reason = self.pushable(patchname)
|
2006-08-09 08:42:50 +04:00
|
|
|
if not pushable:
|
2011-06-13 14:00:56 +04:00
|
|
|
self.explainpushable(patchname, all_patches=True)
|
2006-08-09 08:42:50 +04:00
|
|
|
continue
|
2009-07-11 01:24:35 +04:00
|
|
|
self.ui.status(_("applying %s\n") % patchname)
|
2006-08-17 06:49:45 +04:00
|
|
|
pf = os.path.join(patchdir, patchname)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
try:
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(self.join(patchname), self.plainmode)
|
2011-05-06 19:45:12 +04:00
|
|
|
except IOError:
|
2009-06-21 19:34:33 +04:00
|
|
|
self.ui.warn(_("unable to read %s\n") % patchname)
|
2006-02-28 21:25:26 +03:00
|
|
|
err = 1
|
|
|
|
break
|
|
|
|
|
2008-11-23 01:04:42 +03:00
|
|
|
message = ph.message
|
2006-02-28 21:25:26 +03:00
|
|
|
if not message:
|
2010-10-26 19:59:21 +04:00
|
|
|
# The commit message should not be translated
|
2010-01-21 01:23:18 +03:00
|
|
|
message = "imported patch %s\n" % patchname
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
|
|
|
if list:
|
2010-10-26 19:59:21 +04:00
|
|
|
# The commit message should not be translated
|
2010-01-21 01:23:18 +03:00
|
|
|
message.append("\nimported patch %s" % patchname)
|
2006-02-28 21:25:26 +03:00
|
|
|
message = '\n'.join(message)
|
|
|
|
|
2009-02-17 02:37:23 +03:00
|
|
|
if ph.haspatch:
|
2012-05-11 18:18:47 +04:00
|
|
|
if tobackup:
|
|
|
|
touched = patchmod.changedfiles(self.ui, repo, pf)
|
|
|
|
touched = set(touched) & tobackup
|
2012-05-13 16:00:58 +04:00
|
|
|
if touched and keepchanges:
|
2012-05-12 02:19:30 +04:00
|
|
|
raise AbortNoCleanup(
|
2015-04-21 02:13:44 +03:00
|
|
|
_("conflicting local changes found"),
|
|
|
|
hint=_("did you forget to qrefresh?"))
|
2012-05-11 18:18:47 +04:00
|
|
|
self.backup(repo, touched, copy=True)
|
|
|
|
tobackup = tobackup - touched
|
2009-02-17 02:37:23 +03:00
|
|
|
(patcherr, files, fuzz) = self.patch(repo, pf)
|
2010-03-13 02:02:33 +03:00
|
|
|
if all_files is not None:
|
|
|
|
all_files.update(files)
|
2009-02-17 02:37:23 +03:00
|
|
|
patcherr = not patcherr
|
|
|
|
else:
|
|
|
|
self.ui.warn(_("patch %s is empty\n") % patchname)
|
|
|
|
patcherr, files, fuzz = 0, [], 0
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2006-08-17 06:49:45 +04:00
|
|
|
if merge and files:
|
2007-04-09 11:24:17 +04:00
|
|
|
# Mark as removed/merged and update dirstate parent info
|
|
|
|
removed = []
|
|
|
|
merged = []
|
|
|
|
for f in files:
|
2010-09-20 23:46:56 +04:00
|
|
|
if os.path.lexists(repo.wjoin(f)):
|
2007-04-09 11:24:17 +04:00
|
|
|
merged.append(f)
|
|
|
|
else:
|
|
|
|
removed.append(f)
|
2017-05-19 00:10:53 +03:00
|
|
|
with repo.dirstate.parentchange():
|
|
|
|
for f in removed:
|
|
|
|
repo.dirstate.remove(f)
|
|
|
|
for f in merged:
|
|
|
|
repo.dirstate.merge(f)
|
|
|
|
p1, p2 = repo.dirstate.parents()
|
|
|
|
repo.setparents(p1, merge)
|
2008-05-12 20:37:08 +04:00
|
|
|
|
2013-08-13 03:38:30 +04:00
|
|
|
if all_files and '.hgsubstate' in all_files:
|
2014-04-08 01:10:20 +04:00
|
|
|
wctx = repo[None]
|
|
|
|
pctx = repo['.']
|
2013-08-13 03:38:30 +04:00
|
|
|
overwrite = False
|
2014-04-08 01:10:20 +04:00
|
|
|
mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
|
2013-08-13 03:38:30 +04:00
|
|
|
overwrite)
|
|
|
|
files += mergedsubstate.keys()
|
|
|
|
|
2011-05-13 23:58:24 +04:00
|
|
|
match = scmutil.matchfiles(repo, files or [])
|
2012-02-01 09:13:04 +04:00
|
|
|
oldtip = repo['tip']
|
2012-02-10 23:47:57 +04:00
|
|
|
n = newcommit(repo, None, message, ph.user, ph.date, match=match,
|
|
|
|
force=True)
|
2012-02-01 09:13:04 +04:00
|
|
|
if repo['tip'] == oldtip:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("qpush exactly duplicates child changeset"))
|
2009-05-20 02:52:46 +04:00
|
|
|
if n is None:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("repository commit failed"))
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
if update_status:
|
2010-03-14 02:36:10 +03:00
|
|
|
self.applied.append(statusentry(n, patchname))
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
if patcherr:
|
2015-03-17 15:53:17 +03:00
|
|
|
self.ui.warn(_("patch failed, rejects left in working "
|
|
|
|
"directory\n"))
|
2009-06-21 19:34:33 +04:00
|
|
|
err = 2
|
2006-02-28 21:25:26 +03:00
|
|
|
break
|
|
|
|
|
|
|
|
if fuzz and strict:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("fuzz found when applying patch, stopping\n"))
|
2009-06-21 19:34:33 +04:00
|
|
|
err = 3
|
2006-02-28 21:25:26 +03:00
|
|
|
break
|
|
|
|
return (err, n)
|
|
|
|
|
2009-06-19 15:58:22 +04:00
|
|
|
def _cleanup(self, patches, numrevs, keep=False):
|
|
|
|
if not keep:
|
|
|
|
r = self.qrepo()
|
|
|
|
if r:
|
2011-05-27 02:15:35 +04:00
|
|
|
r[None].forget(patches)
|
|
|
|
for p in patches:
|
2012-12-09 03:25:21 +04:00
|
|
|
try:
|
|
|
|
os.unlink(self.join(p))
|
2015-06-24 08:20:08 +03:00
|
|
|
except OSError as inst:
|
2012-12-09 03:25:21 +04:00
|
|
|
if inst.errno != errno.ENOENT:
|
|
|
|
raise
|
2009-06-19 15:58:22 +04:00
|
|
|
|
2012-01-18 20:06:59 +04:00
|
|
|
qfinished = []
|
2009-06-19 15:58:22 +04:00
|
|
|
if numrevs:
|
2011-04-25 16:03:12 +04:00
|
|
|
qfinished = self.applied[:numrevs]
|
2009-06-19 15:58:22 +04:00
|
|
|
del self.applied[:numrevs]
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2009-06-19 15:58:22 +04:00
|
|
|
|
2011-04-25 16:03:12 +04:00
|
|
|
unknown = []
|
|
|
|
|
2011-06-13 13:54:25 +04:00
|
|
|
for (i, p) in sorted([(self.findseries(p), p) for p in patches],
|
2011-04-25 16:03:12 +04:00
|
|
|
reverse=True):
|
|
|
|
if i is not None:
|
2011-06-13 13:06:11 +04:00
|
|
|
del self.fullseries[i]
|
2011-04-25 16:03:12 +04:00
|
|
|
else:
|
|
|
|
unknown.append(p)
|
|
|
|
|
|
|
|
if unknown:
|
|
|
|
if numrevs:
|
|
|
|
rev = dict((entry.name, entry.node) for entry in qfinished)
|
|
|
|
for p in unknown:
|
|
|
|
msg = _('revision %s refers to unknown patches: %s\n')
|
|
|
|
self.ui.warn(msg % (short(rev[p]), p))
|
|
|
|
else:
|
|
|
|
msg = _('unknown patches: %s\n')
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(''.join(msg % p for p in unknown))
|
2011-04-25 16:03:12 +04:00
|
|
|
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2012-01-11 05:28:36 +04:00
|
|
|
self.seriesdirty = True
|
2012-01-18 20:06:59 +04:00
|
|
|
return [entry.node for entry in qfinished]
|
2008-06-03 11:34:14 +04:00
|
|
|
|
2009-06-19 15:58:22 +04:00
|
|
|
def _revpatches(self, repo, revs):
|
2010-03-14 02:36:10 +03:00
|
|
|
firstrev = repo[self.applied[0].node].rev()
|
2008-06-03 11:34:14 +04:00
|
|
|
patches = []
|
2009-06-19 15:58:22 +04:00
|
|
|
for i, rev in enumerate(revs):
|
2009-06-19 14:37:29 +04:00
|
|
|
|
2008-06-03 11:34:14 +04:00
|
|
|
if rev < firstrev:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d is not managed') % rev)
|
2009-06-19 14:37:29 +04:00
|
|
|
|
|
|
|
ctx = repo[rev]
|
2010-03-14 02:36:10 +03:00
|
|
|
base = self.applied[i].node
|
2009-06-19 14:37:29 +04:00
|
|
|
if ctx.node() != base:
|
|
|
|
msg = _('cannot delete revision %d above applied patches')
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(msg % rev)
|
2009-06-19 14:37:29 +04:00
|
|
|
|
2009-06-19 15:58:22 +04:00
|
|
|
patch = self.applied[i].name
|
2009-06-19 14:37:29 +04:00
|
|
|
for fmt in ('[mq]: %s', 'imported patch %s'):
|
|
|
|
if ctx.description() == fmt % patch:
|
|
|
|
msg = _('patch %s finalized without changeset message\n')
|
|
|
|
repo.ui.status(msg % patch)
|
|
|
|
break
|
|
|
|
|
2009-06-19 15:58:22 +04:00
|
|
|
patches.append(patch)
|
|
|
|
return patches
|
2008-06-03 11:34:14 +04:00
|
|
|
|
2009-06-19 15:58:22 +04:00
|
|
|
def finish(self, repo, revs):
|
2012-01-30 18:17:08 +04:00
|
|
|
# Manually trigger phase computation to ensure phasedefaults is
|
|
|
|
# executed before we remove the patches.
|
2012-05-12 02:24:07 +04:00
|
|
|
repo._phasecache
|
2009-06-19 15:58:22 +04:00
|
|
|
patches = self._revpatches(repo, sorted(revs))
|
2012-01-18 20:06:59 +04:00
|
|
|
qfinished = self._cleanup(patches, len(patches))
|
2012-01-30 18:17:08 +04:00
|
|
|
if qfinished and repo.ui.configbool('mq', 'secret', False):
|
|
|
|
# only use this logic when the secret option is added
|
2012-01-18 20:06:59 +04:00
|
|
|
oldqbase = repo[qfinished[0]]
|
2012-03-24 15:06:49 +04:00
|
|
|
tphase = repo.ui.config('phases', 'new-commit', phases.draft)
|
|
|
|
if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.transaction('qfinish') as tr:
|
2014-08-06 12:54:19 +04:00
|
|
|
phases.advanceboundary(repo, tr, tphase, qfinished)
|
2008-06-03 11:34:14 +04:00
|
|
|
|
2006-09-14 17:35:55 +04:00
|
|
|
def delete(self, repo, patches, opts):
|
2007-06-28 19:42:43 +04:00
|
|
|
if not patches and not opts.get('rev'):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('qdelete requires at least one revision or '
|
2007-06-28 20:19:08 +04:00
|
|
|
'patch name'))
|
2007-06-28 19:42:43 +04:00
|
|
|
|
2010-06-17 06:00:02 +04:00
|
|
|
realpatches = []
|
2006-08-14 22:41:08 +04:00
|
|
|
for patch in patches:
|
|
|
|
patch = self.lookup(patch, strict=True)
|
|
|
|
info = self.isapplied(patch)
|
2006-10-13 00:24:09 +04:00
|
|
|
if info:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("cannot delete applied patch %s") % patch)
|
2006-08-14 22:41:08 +04:00
|
|
|
if patch not in self.series:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch %s not in series file") % patch)
|
2010-10-09 19:36:50 +04:00
|
|
|
if patch not in realpatches:
|
|
|
|
realpatches.append(patch)
|
2006-08-14 22:41:08 +04:00
|
|
|
|
2009-06-19 15:58:22 +04:00
|
|
|
numrevs = 0
|
2006-10-13 00:24:09 +04:00
|
|
|
if opts.get('rev'):
|
|
|
|
if not self.applied:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no patches applied'))
|
2011-05-13 23:06:28 +04:00
|
|
|
revs = scmutil.revrange(repo, opts.get('rev'))
|
2014-10-07 11:12:56 +04:00
|
|
|
revs.sort()
|
2009-06-19 15:58:22 +04:00
|
|
|
revpatches = self._revpatches(repo, revs)
|
2010-06-17 06:00:02 +04:00
|
|
|
realpatches += revpatches
|
2009-06-19 15:58:22 +04:00
|
|
|
numrevs = len(revpatches)
|
2006-08-14 22:41:08 +04:00
|
|
|
|
2010-06-17 06:00:02 +04:00
|
|
|
self._cleanup(realpatches, numrevs, opts.get('keep'))
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2011-06-13 14:03:38 +04:00
|
|
|
def checktoppatch(self, repo):
|
2013-01-15 04:05:12 +04:00
|
|
|
'''check that working directory is at qtip'''
|
2010-03-14 16:45:01 +03:00
|
|
|
if self.applied:
|
2010-03-14 02:36:10 +03:00
|
|
|
top = self.applied[-1].node
|
2010-01-01 23:49:05 +03:00
|
|
|
patch = self.applied[-1].name
|
2013-01-15 04:05:12 +04:00
|
|
|
if repo.dirstate.p1() != top:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("working directory revision is not qtip"))
|
2010-01-01 23:49:05 +03:00
|
|
|
return top, patch
|
|
|
|
return None, None
|
|
|
|
|
2012-06-27 17:03:27 +04:00
|
|
|
def putsubstate2changes(self, substatestate, changes):
|
|
|
|
for files in changes[:3]:
|
|
|
|
if '.hgsubstate' in files:
|
|
|
|
return # already listed up
|
|
|
|
# not yet listed up
|
|
|
|
if substatestate in 'a?':
|
|
|
|
changes[1].append('.hgsubstate')
|
|
|
|
elif substatestate in 'r':
|
|
|
|
changes[2].append('.hgsubstate')
|
|
|
|
else: # modified
|
|
|
|
changes[0].append('.hgsubstate')
|
|
|
|
|
2011-06-13 14:06:32 +04:00
|
|
|
def checklocalchanges(self, repo, force=False, refresh=True):
|
2013-09-25 14:28:40 +04:00
|
|
|
excsuffix = ''
|
|
|
|
if refresh:
|
2015-10-14 10:30:27 +03:00
|
|
|
excsuffix = ', qrefresh first'
|
2013-09-25 14:28:40 +04:00
|
|
|
# plain versions for i18n tool to detect them
|
2015-10-14 10:30:27 +03:00
|
|
|
_("local changes found, qrefresh first")
|
|
|
|
_("local changed subrepos found, qrefresh first")
|
2013-09-25 14:43:14 +04:00
|
|
|
return checklocalchanges(repo, force, excsuffix)
|
2007-06-25 22:13:27 +04:00
|
|
|
|
2011-04-29 23:21:13 +04:00
|
|
|
_reserved = ('series', 'status', 'guards', '.', '..')
|
2011-06-13 14:07:46 +04:00
|
|
|
def checkreservedname(self, name):
|
2011-04-29 23:21:13 +04:00
|
|
|
if name in self._reserved:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('"%s" cannot be used as the name of a patch')
|
2008-02-04 02:03:46 +03:00
|
|
|
% name)
|
2017-03-20 05:38:37 +03:00
|
|
|
if name != name.strip():
|
|
|
|
# whitespace is stripped by parseseries()
|
|
|
|
raise error.Abort(_('patch name cannot begin or end with '
|
|
|
|
'whitespace'))
|
2011-04-29 23:21:13 +04:00
|
|
|
for prefix in ('.hg', '.mq'):
|
|
|
|
if name.startswith(prefix):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('patch name cannot begin with "%s"')
|
2011-04-29 23:21:13 +04:00
|
|
|
% prefix)
|
2015-06-05 21:34:58 +03:00
|
|
|
for c in ('#', ':', '\r', '\n'):
|
2011-04-29 23:21:13 +04:00
|
|
|
if c in name:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('%r cannot be used in the name of a patch')
|
2011-04-29 23:21:13 +04:00
|
|
|
% c)
|
|
|
|
|
2011-05-24 20:16:51 +04:00
|
|
|
def checkpatchname(self, name, force=False):
|
2011-06-13 14:07:46 +04:00
|
|
|
self.checkreservedname(name)
|
2011-05-24 20:16:51 +04:00
|
|
|
if not force and os.path.exists(self.join(name)):
|
|
|
|
if os.path.isdir(self.join(name)):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('"%s" already exists as a directory')
|
2011-05-24 20:16:51 +04:00
|
|
|
% name)
|
|
|
|
else:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('patch "%s" already exists') % name)
|
2008-02-04 02:03:46 +03:00
|
|
|
|
2016-01-19 08:00:30 +03:00
|
|
|
def makepatchname(self, title, fallbackname):
|
|
|
|
"""Return a suitable filename for title, adding a suffix to make
|
|
|
|
it unique in the existing list"""
|
|
|
|
namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
|
2016-03-10 01:21:08 +03:00
|
|
|
namebase = namebase[:75] # avoid too long name (issue5117)
|
2016-01-19 08:00:59 +03:00
|
|
|
if namebase:
|
|
|
|
try:
|
|
|
|
self.checkreservedname(namebase)
|
|
|
|
except error.Abort:
|
|
|
|
namebase = fallbackname
|
|
|
|
else:
|
2016-01-19 08:00:30 +03:00
|
|
|
namebase = fallbackname
|
|
|
|
name = namebase
|
|
|
|
i = 0
|
2016-01-19 08:00:59 +03:00
|
|
|
while True:
|
|
|
|
if name not in self.fullseries:
|
|
|
|
try:
|
|
|
|
self.checkpatchname(name)
|
|
|
|
break
|
|
|
|
except error.Abort:
|
|
|
|
pass
|
2016-01-19 08:00:30 +03:00
|
|
|
i += 1
|
|
|
|
name = '%s__%s' % (namebase, i)
|
|
|
|
return name
|
|
|
|
|
2012-05-13 16:00:58 +04:00
|
|
|
def checkkeepchanges(self, keepchanges, force):
|
|
|
|
if force and keepchanges:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot use both --force and --keep-changes'))
|
2012-05-12 02:19:30 +04:00
|
|
|
|
2008-10-19 08:52:22 +04:00
|
|
|
def new(self, repo, patchfn, *pats, **opts):
|
2008-10-19 03:33:10 +04:00
|
|
|
"""options:
|
|
|
|
msg: a string or a no-argument function returning a string
|
|
|
|
"""
|
2007-06-25 22:13:27 +04:00
|
|
|
msg = opts.get('msg')
|
2014-05-10 19:49:36 +04:00
|
|
|
edit = opts.get('edit')
|
2014-08-02 16:46:26 +04:00
|
|
|
editform = opts.get('editform', 'mq.qnew')
|
2007-12-20 00:36:18 +03:00
|
|
|
user = opts.get('user')
|
2008-01-02 18:24:13 +03:00
|
|
|
date = opts.get('date')
|
2008-02-17 23:34:28 +03:00
|
|
|
if date:
|
|
|
|
date = util.parsedate(date)
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = self.diffopts({'git': opts.get('git')})
|
2011-05-24 20:17:02 +04:00
|
|
|
if opts.get('checkname', True):
|
|
|
|
self.checkpatchname(patchfn)
|
2013-09-25 13:24:43 +04:00
|
|
|
inclsubs = checksubstate(repo)
|
2010-12-08 07:14:43 +03:00
|
|
|
if inclsubs:
|
2012-04-05 18:52:55 +04:00
|
|
|
substatestate = repo.dirstate['.hgsubstate']
|
2007-06-25 22:13:27 +04:00
|
|
|
if opts.get('include') or opts.get('exclude') or pats:
|
2008-10-19 07:45:53 +04:00
|
|
|
# detect missing files in pats
|
|
|
|
def badfn(f, msg):
|
2010-12-08 07:14:43 +03:00
|
|
|
if f != '.hgsubstate': # .hgsubstate is auto-created
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort('%s: %s' % (f, msg))
|
2015-06-06 02:35:32 +03:00
|
|
|
match = scmutil.match(repo[None], pats, opts, badfn=badfn)
|
2012-04-05 18:52:55 +04:00
|
|
|
changes = repo.status(match=match)
|
2007-06-25 22:13:27 +04:00
|
|
|
else:
|
2012-04-05 18:52:55 +04:00
|
|
|
changes = self.checklocalchanges(repo, force=True)
|
2014-03-19 19:10:45 +04:00
|
|
|
commitfiles = list(inclsubs)
|
|
|
|
for files in changes[:3]:
|
localrepo: omit ".hgsubstate" also from "added" files
Before this patch, "localrepository.commit()" omits ".hgsubstate" from
"modified" (changes[0]) and "removed" (changes[2]) file list before
checking subrepositories, but leaves one in "added" (changes[1]) as it
is.
Then, "localrepository.commit()" adds ".hgsubstate" into "modified" or
"removed" list forcibly, according to subrepository statuses.
If "added" contains ".hgsubstate", the committed context will contain
two ".hgsubstate" in its "files": one from "added" (not omitted one),
and another from "modified" or "removed" (newly added one).
How many times ".hgsubstate" appears in "files" changes node hash,
even though revision content is same, because node hash calculation
uses the specified "files" directly (without duplication check or so).
This means that node hash of committed revision changes according to
existence of ".hgsubstate" in "added" at "localrepository.commit()".
".hgsubstate" is treated as "added", not only in accidental cases, but
also in the case of "qpush" for the patch adding ".hgsubstate".
This patch omits ".hgsubstate" also from "added" files before checking
subrepositories. This patch also omits ".hgsubstate" exclusion in
"qnew"/"qrefresh" introduced by changeset bbb8109a634f, because this
patch makes them meaningless.
"hg parents --template '{files}\n'" newly added to "test-mq-subrepo.t"
enhances checking unexpected multiple appearances of ".hgsubstate" in
"files" of created/refreshed MQ revisions.
2014-03-22 18:39:51 +04:00
|
|
|
commitfiles.extend(files)
|
2014-03-19 19:10:45 +04:00
|
|
|
match = scmutil.matchfiles(repo, commitfiles)
|
2010-02-07 16:37:05 +03:00
|
|
|
if len(repo[None].parents()) > 1:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot manage merge changesets'))
|
2011-06-13 14:03:38 +04:00
|
|
|
self.checktoppatch(repo)
|
2011-06-13 14:09:29 +04:00
|
|
|
insert = self.fullseriesend()
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.wlock():
|
2010-10-29 17:25:21 +04:00
|
|
|
try:
|
|
|
|
# if patch file write fails, abort early
|
|
|
|
p = self.opener(patchfn, "w")
|
2015-06-24 08:20:08 +03:00
|
|
|
except IOError as e:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot write patch "%s": %s')
|
2010-10-29 17:25:21 +04:00
|
|
|
% (patchfn, e.strerror))
|
2008-10-19 08:52:22 +04:00
|
|
|
try:
|
2014-05-05 16:26:40 +04:00
|
|
|
defaultmsg = "[mq]: %s" % patchfn
|
2014-08-02 16:46:26 +04:00
|
|
|
editor = cmdutil.getcommiteditor(editform=editform)
|
2014-05-10 19:49:36 +04:00
|
|
|
if edit:
|
2014-05-10 19:49:36 +04:00
|
|
|
def finishdesc(desc):
|
2014-05-05 16:26:40 +04:00
|
|
|
if desc.rstrip():
|
|
|
|
return desc
|
|
|
|
else:
|
|
|
|
return defaultmsg
|
2014-05-10 19:49:36 +04:00
|
|
|
# i18n: this message is shown in editor with "HG: " prefix
|
|
|
|
extramsg = _('Leave message empty to use default message.')
|
|
|
|
editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
|
2014-08-02 16:46:26 +04:00
|
|
|
extramsg=extramsg,
|
|
|
|
editform=editform)
|
2014-05-05 16:26:40 +04:00
|
|
|
commitmsg = msg
|
|
|
|
else:
|
|
|
|
commitmsg = msg or defaultmsg
|
|
|
|
|
2012-02-10 23:47:57 +04:00
|
|
|
n = newcommit(repo, None, commitmsg, user, date, match=match,
|
2014-05-05 16:26:40 +04:00
|
|
|
force=True, editor=editor)
|
2009-05-20 02:52:46 +04:00
|
|
|
if n is None:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("repo commit failed"))
|
2008-10-19 08:52:22 +04:00
|
|
|
try:
|
2011-06-13 13:06:11 +04:00
|
|
|
self.fullseries[insert:insert] = [patchfn]
|
2010-03-14 02:36:10 +03:00
|
|
|
self.applied.append(statusentry(n, patchfn))
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2012-01-11 05:28:36 +04:00
|
|
|
self.seriesdirty = True
|
|
|
|
self.applieddirty = True
|
2014-05-05 16:26:40 +04:00
|
|
|
nctx = repo[n]
|
2014-09-20 19:06:57 +04:00
|
|
|
ph = patchheader(self.join(patchfn), self.plainmode)
|
|
|
|
if user:
|
|
|
|
ph.setuser(user)
|
|
|
|
if date:
|
|
|
|
ph.setdate('%s %s' % date)
|
|
|
|
ph.setparent(hex(nctx.p1().node()))
|
|
|
|
msg = nctx.description().strip()
|
|
|
|
if msg == defaultmsg.strip():
|
|
|
|
msg = ''
|
|
|
|
ph.setmessage(msg)
|
|
|
|
p.write(str(ph))
|
2008-10-19 08:52:22 +04:00
|
|
|
if commitfiles:
|
|
|
|
parent = self.qparents(repo, n)
|
2012-04-05 18:52:55 +04:00
|
|
|
if inclsubs:
|
2012-06-27 17:03:27 +04:00
|
|
|
self.putsubstate2changes(substatestate, changes)
|
2011-05-06 20:03:45 +04:00
|
|
|
chunks = patchmod.diff(repo, node1=parent, node2=n,
|
2012-04-05 18:52:55 +04:00
|
|
|
changes=changes, opts=diffopts)
|
2008-11-03 18:48:23 +03:00
|
|
|
for chunk in chunks:
|
|
|
|
p.write(chunk)
|
2008-10-19 08:52:22 +04:00
|
|
|
p.close()
|
|
|
|
r = self.qrepo()
|
2010-01-25 09:05:27 +03:00
|
|
|
if r:
|
2010-06-07 22:03:32 +04:00
|
|
|
r[None].add([patchfn])
|
2012-05-13 15:18:06 +04:00
|
|
|
except: # re-raises
|
2008-10-19 08:52:22 +04:00
|
|
|
repo.rollback()
|
|
|
|
raise
|
2008-10-28 21:25:26 +03:00
|
|
|
except Exception:
|
2008-10-19 08:52:22 +04:00
|
|
|
patchpath = self.join(patchfn)
|
|
|
|
try:
|
|
|
|
os.unlink(patchpath)
|
2012-05-12 18:02:45 +04:00
|
|
|
except OSError:
|
2008-10-19 08:52:22 +04:00
|
|
|
self.ui.warn(_('error unlinking %s\n') % patchpath)
|
|
|
|
raise
|
2007-07-22 01:02:10 +04:00
|
|
|
self.removeundo(repo)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
def isapplied(self, patch):
|
|
|
|
"""returns (index, rev, patch)"""
|
2009-05-27 00:59:52 +04:00
|
|
|
for i, a in enumerate(self.applied):
|
2006-08-03 22:09:12 +04:00
|
|
|
if a.name == patch:
|
2010-03-14 02:36:10 +03:00
|
|
|
return (i, a.node, a.name)
|
2006-02-28 21:25:26 +03:00
|
|
|
return None
|
|
|
|
|
2006-10-01 21:26:33 +04:00
|
|
|
# if the exact patch name does not exist, we try a few
|
2006-07-27 20:27:35 +04:00
|
|
|
# variations. If strict is passed, we try only #1
|
|
|
|
#
|
2010-10-31 20:29:56 +03:00
|
|
|
# 1) a number (as string) to indicate an offset in the series file
|
2006-07-27 20:27:35 +04:00
|
|
|
# 2) a unique substring of the patch name was given
|
|
|
|
# 3) patchname[-+]num to indicate an offset in the series file
|
|
|
|
def lookup(self, patch, strict=False):
|
2011-06-13 15:48:47 +04:00
|
|
|
def partialname(s):
|
2006-07-27 20:27:35 +04:00
|
|
|
if s in self.series:
|
|
|
|
return s
|
2006-08-02 17:36:27 +04:00
|
|
|
matches = [x for x in self.series if s in x]
|
|
|
|
if len(matches) > 1:
|
|
|
|
self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
|
|
|
|
for m in matches:
|
|
|
|
self.ui.warn(' %s\n' % m)
|
|
|
|
return None
|
|
|
|
if matches:
|
|
|
|
return matches[0]
|
2010-03-14 16:45:01 +03:00
|
|
|
if self.series and self.applied:
|
2006-07-27 20:27:35 +04:00
|
|
|
if s == 'qtip':
|
2012-12-10 02:33:16 +04:00
|
|
|
return self.series[self.seriesend(True) - 1]
|
2006-07-27 20:27:35 +04:00
|
|
|
if s == 'qbase':
|
|
|
|
return self.series[0]
|
|
|
|
return None
|
2009-01-06 12:30:22 +03:00
|
|
|
|
|
|
|
if patch in self.series:
|
|
|
|
return patch
|
2006-07-27 20:27:35 +04:00
|
|
|
|
2006-08-09 05:12:48 +04:00
|
|
|
if not os.path.isfile(self.join(patch)):
|
2006-02-28 21:25:26 +03:00
|
|
|
try:
|
|
|
|
sno = int(patch)
|
2010-01-25 09:05:27 +03:00
|
|
|
except (ValueError, OverflowError):
|
2006-07-27 20:27:35 +04:00
|
|
|
pass
|
|
|
|
else:
|
2009-01-06 12:30:22 +03:00
|
|
|
if -len(self.series) <= sno < len(self.series):
|
2006-08-09 08:42:50 +04:00
|
|
|
return self.series[sno]
|
2009-01-06 12:30:22 +03:00
|
|
|
|
2006-07-27 20:27:35 +04:00
|
|
|
if not strict:
|
2011-06-13 15:48:47 +04:00
|
|
|
res = partialname(patch)
|
2006-07-27 20:27:35 +04:00
|
|
|
if res:
|
|
|
|
return res
|
2006-09-12 19:27:41 +04:00
|
|
|
minus = patch.rfind('-')
|
|
|
|
if minus >= 0:
|
2011-06-13 15:48:47 +04:00
|
|
|
res = partialname(patch[:minus])
|
2006-07-27 20:27:35 +04:00
|
|
|
if res:
|
|
|
|
i = self.series.index(res)
|
|
|
|
try:
|
2010-01-25 09:05:27 +03:00
|
|
|
off = int(patch[minus + 1:] or 1)
|
|
|
|
except (ValueError, OverflowError):
|
2006-07-27 20:27:35 +04:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if i - off >= 0:
|
|
|
|
return self.series[i - off]
|
2006-09-12 19:27:41 +04:00
|
|
|
plus = patch.rfind('+')
|
|
|
|
if plus >= 0:
|
2011-06-13 15:48:47 +04:00
|
|
|
res = partialname(patch[:plus])
|
2006-07-27 20:27:35 +04:00
|
|
|
if res:
|
|
|
|
i = self.series.index(res)
|
|
|
|
try:
|
2010-01-25 09:05:27 +03:00
|
|
|
off = int(patch[plus + 1:] or 1)
|
|
|
|
except (ValueError, OverflowError):
|
2006-07-27 20:27:35 +04:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if i + off < len(self.series):
|
|
|
|
return self.series[i + off]
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch %s not in series") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2012-05-12 02:19:30 +04:00
|
|
|
def push(self, repo, patch=None, force=False, list=False, mergeq=None,
|
2012-05-13 16:00:58 +04:00
|
|
|
all=False, move=False, exact=False, nobackup=False,
|
|
|
|
keepchanges=False):
|
|
|
|
self.checkkeepchanges(keepchanges, force)
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = self.diffopts()
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.wlock():
|
2013-11-25 03:29:10 +04:00
|
|
|
heads = []
|
|
|
|
for hs in repo.branchmap().itervalues():
|
|
|
|
heads.extend(hs)
|
2010-02-07 16:06:05 +03:00
|
|
|
if not heads:
|
|
|
|
heads = [nullid]
|
2011-04-05 01:21:59 +04:00
|
|
|
if repo.dirstate.p1() not in heads and not exact:
|
2009-06-08 22:02:44 +04:00
|
|
|
self.ui.status(_("(working directory not at a head)\n"))
|
2008-03-21 23:44:11 +03:00
|
|
|
|
2009-06-08 22:02:44 +04:00
|
|
|
if not self.series:
|
|
|
|
self.ui.warn(_('no patches in series\n'))
|
|
|
|
return 0
|
2008-11-23 11:44:31 +03:00
|
|
|
|
2007-07-22 01:02:10 +04:00
|
|
|
# Suppose our series file is: A B C and the current 'top'
|
|
|
|
# patch is B. qpush C should be performed (moving forward)
|
|
|
|
# qpush B is a NOP (no change) qpush A is an error (can't
|
|
|
|
# go backwards with qpush)
|
|
|
|
if patch:
|
2011-10-14 04:50:06 +04:00
|
|
|
patch = self.lookup(patch)
|
2007-07-22 01:02:10 +04:00
|
|
|
info = self.isapplied(patch)
|
2011-02-12 11:08:41 +03:00
|
|
|
if info and info[0] >= len(self.applied) - 1:
|
2008-11-23 11:44:31 +03:00
|
|
|
self.ui.warn(
|
|
|
|
_('qpush: %s is already at the top\n') % patch)
|
2010-06-22 23:36:09 +04:00
|
|
|
return 0
|
2011-02-12 11:08:41 +03:00
|
|
|
|
2008-11-23 11:44:31 +03:00
|
|
|
pushable, reason = self.pushable(patch)
|
2011-02-12 11:08:41 +03:00
|
|
|
if pushable:
|
2011-06-13 14:11:49 +04:00
|
|
|
if self.series.index(patch) < self.seriesend():
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(
|
2011-02-12 11:08:41 +03:00
|
|
|
_("cannot push to a previous patch: %s") % patch)
|
|
|
|
else:
|
2008-11-23 11:44:31 +03:00
|
|
|
if reason:
|
2011-05-31 10:47:16 +04:00
|
|
|
reason = _('guarded by %s') % reason
|
2008-11-23 11:44:31 +03:00
|
|
|
else:
|
|
|
|
reason = _('no matching guards')
|
|
|
|
self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
|
|
|
|
return 1
|
|
|
|
elif all:
|
|
|
|
patch = self.series[-1]
|
|
|
|
if self.isapplied(patch):
|
|
|
|
self.ui.warn(_('all patches are currently applied\n'))
|
|
|
|
return 0
|
2007-07-22 01:02:10 +04:00
|
|
|
|
|
|
|
# Following the above example, starting at 'top' of B:
|
|
|
|
# qpush should be performed (pushes C), but a subsequent
|
|
|
|
# qpush without an argument is an error (nothing to
|
|
|
|
# apply). This allows a loop of "...while hg qpush..." to
|
|
|
|
# work as it detects an error when done
|
2011-06-13 14:11:49 +04:00
|
|
|
start = self.seriesend()
|
2008-11-23 11:44:31 +03:00
|
|
|
if start == len(self.series):
|
2007-07-22 01:02:10 +04:00
|
|
|
self.ui.warn(_('patch series already fully applied\n'))
|
|
|
|
return 1
|
2012-05-13 16:00:58 +04:00
|
|
|
if not force and not keepchanges:
|
2011-06-25 00:25:42 +04:00
|
|
|
self.checklocalchanges(repo, refresh=self.applied)
|
2007-07-22 01:02:10 +04:00
|
|
|
|
2010-11-18 05:18:44 +03:00
|
|
|
if exact:
|
2012-05-13 16:00:58 +04:00
|
|
|
if keepchanges:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(
|
2012-05-13 16:00:58 +04:00
|
|
|
_("cannot use --exact and --keep-changes together"))
|
2010-11-18 05:18:44 +03:00
|
|
|
if move:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot use --exact and --move '
|
2012-05-12 17:54:54 +04:00
|
|
|
'together'))
|
2010-11-18 05:18:44 +03:00
|
|
|
if self.applied:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot push --exact with applied '
|
2012-05-12 17:54:54 +04:00
|
|
|
'patches'))
|
2010-11-18 05:18:44 +03:00
|
|
|
root = self.series[start]
|
|
|
|
target = patchheader(self.join(root), self.plainmode).parent
|
|
|
|
if not target:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(
|
2012-03-08 23:35:27 +04:00
|
|
|
_("%s does not have a parent recorded") % root)
|
2010-11-18 05:18:44 +03:00
|
|
|
if not repo[target] == repo['.']:
|
|
|
|
hg.update(repo, target)
|
|
|
|
|
2010-04-30 17:05:51 +04:00
|
|
|
if move:
|
2010-07-10 23:23:00 +04:00
|
|
|
if not patch:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("please specify the patch to move"))
|
2012-03-29 02:35:00 +04:00
|
|
|
for fullstart, rpn in enumerate(self.fullseries):
|
|
|
|
# strip markers for patch guards
|
|
|
|
if self.guard_re.split(rpn, 1)[0] == self.series[start]:
|
|
|
|
break
|
|
|
|
for i, rpn in enumerate(self.fullseries[fullstart:]):
|
2010-07-10 23:23:00 +04:00
|
|
|
# strip markers for patch guards
|
|
|
|
if self.guard_re.split(rpn, 1)[0] == patch:
|
|
|
|
break
|
2012-03-29 02:35:00 +04:00
|
|
|
index = fullstart + i
|
2011-06-13 13:06:11 +04:00
|
|
|
assert index < len(self.fullseries)
|
|
|
|
fullpatch = self.fullseries[index]
|
|
|
|
del self.fullseries[index]
|
2012-03-29 02:35:00 +04:00
|
|
|
self.fullseries.insert(fullstart, fullpatch)
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2012-01-11 05:28:36 +04:00
|
|
|
self.seriesdirty = True
|
2010-04-30 17:05:51 +04:00
|
|
|
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2007-07-22 01:02:10 +04:00
|
|
|
if start > 0:
|
2011-06-13 14:03:38 +04:00
|
|
|
self.checktoppatch(repo)
|
2007-07-22 01:02:10 +04:00
|
|
|
if not patch:
|
|
|
|
patch = self.series[start]
|
|
|
|
end = start + 1
|
2007-05-08 23:16:02 +04:00
|
|
|
else:
|
2007-07-22 01:02:10 +04:00
|
|
|
end = self.series.index(patch, start) + 1
|
2009-06-21 19:34:33 +04:00
|
|
|
|
2012-05-11 18:18:47 +04:00
|
|
|
tobackup = set()
|
2012-05-13 16:00:58 +04:00
|
|
|
if (not nobackup and force) or keepchanges:
|
2014-10-05 07:53:05 +04:00
|
|
|
status = self.checklocalchanges(repo, force=True)
|
2012-05-13 16:00:58 +04:00
|
|
|
if keepchanges:
|
2014-10-05 07:53:05 +04:00
|
|
|
tobackup.update(status.modified + status.added +
|
|
|
|
status.removed + status.deleted)
|
2012-05-12 02:19:30 +04:00
|
|
|
else:
|
2014-10-05 07:53:05 +04:00
|
|
|
tobackup.update(status.modified + status.added)
|
2012-05-11 18:18:47 +04:00
|
|
|
|
2007-07-22 01:02:10 +04:00
|
|
|
s = self.series[start:end]
|
2010-03-13 02:02:33 +03:00
|
|
|
all_files = set()
|
2007-07-22 01:02:10 +04:00
|
|
|
try:
|
|
|
|
if mergeq:
|
2010-01-01 21:53:05 +03:00
|
|
|
ret = self.mergepatch(repo, mergeq, s, diffopts)
|
2007-07-22 01:02:10 +04:00
|
|
|
else:
|
2012-05-11 18:18:47 +04:00
|
|
|
ret = self.apply(repo, s, list, all_files=all_files,
|
2012-05-13 16:00:58 +04:00
|
|
|
tobackup=tobackup, keepchanges=keepchanges)
|
2015-04-21 02:13:44 +03:00
|
|
|
except AbortNoCleanup:
|
|
|
|
raise
|
2012-05-13 15:18:06 +04:00
|
|
|
except: # re-raises
|
2015-10-12 11:37:09 +03:00
|
|
|
self.ui.warn(_('cleaning up working directory...\n'))
|
|
|
|
cmdutil.revert(self.ui, repo, repo['.'],
|
|
|
|
repo.dirstate.parents(), no_backup=True)
|
2007-07-22 01:02:10 +04:00
|
|
|
# only remove unknown files that we know we touched or
|
|
|
|
# created while patching
|
2010-03-13 02:08:06 +03:00
|
|
|
for f in all_files:
|
|
|
|
if f not in repo.dirstate:
|
2015-01-14 03:15:26 +03:00
|
|
|
repo.wvfs.unlinkpath(f, ignoremissing=True)
|
2007-07-22 01:02:10 +04:00
|
|
|
self.ui.warn(_('done\n'))
|
|
|
|
raise
|
2009-06-21 19:34:33 +04:00
|
|
|
|
2009-10-16 02:09:18 +04:00
|
|
|
if not self.applied:
|
|
|
|
return ret[0]
|
2007-07-22 01:02:10 +04:00
|
|
|
top = self.applied[-1].name
|
2009-06-21 19:34:33 +04:00
|
|
|
if ret[0] and ret[0] > 1:
|
2015-10-14 10:30:27 +03:00
|
|
|
msg = _("errors during apply, please fix and qrefresh %s\n")
|
2009-06-21 19:34:33 +04:00
|
|
|
self.ui.write(msg % top)
|
2007-07-22 01:02:10 +04:00
|
|
|
else:
|
2009-01-03 19:15:21 +03:00
|
|
|
self.ui.write(_("now at: %s\n") % top)
|
2007-07-22 01:02:10 +04:00
|
|
|
return ret[0]
|
2009-06-21 19:34:33 +04:00
|
|
|
|
2012-05-11 18:57:26 +04:00
|
|
|
def pop(self, repo, patch=None, force=False, update=True, all=False,
|
2012-05-13 16:00:58 +04:00
|
|
|
nobackup=False, keepchanges=False):
|
|
|
|
self.checkkeepchanges(keepchanges, force)
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.wlock():
|
2007-07-22 01:02:10 +04:00
|
|
|
if patch:
|
|
|
|
# index, rev, patch
|
|
|
|
info = self.isapplied(patch)
|
|
|
|
if not info:
|
|
|
|
patch = self.lookup(patch)
|
|
|
|
info = self.isapplied(patch)
|
|
|
|
if not info:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch %s is not applied") % patch)
|
2007-02-13 16:54:48 +03:00
|
|
|
|
2010-03-14 16:45:01 +03:00
|
|
|
if not self.applied:
|
2007-07-22 01:02:10 +04:00
|
|
|
# Allow qpop -a to work repeatedly,
|
|
|
|
# but not qpop without an argument
|
|
|
|
self.ui.warn(_("no patches applied\n"))
|
|
|
|
return not all
|
|
|
|
|
2009-01-12 11:53:55 +03:00
|
|
|
if all:
|
|
|
|
start = 0
|
|
|
|
elif patch:
|
|
|
|
start = info[0] + 1
|
|
|
|
else:
|
|
|
|
start = len(self.applied) - 1
|
|
|
|
|
|
|
|
if start >= len(self.applied):
|
|
|
|
self.ui.warn(_("qpop: %s is already at the top\n") % patch)
|
|
|
|
return
|
|
|
|
|
2007-07-22 01:02:10 +04:00
|
|
|
if not update:
|
|
|
|
parents = repo.dirstate.parents()
|
2010-03-14 02:36:10 +03:00
|
|
|
rr = [x.node for x in self.applied]
|
2007-07-22 01:02:10 +04:00
|
|
|
for p in parents:
|
|
|
|
if p in rr:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("qpop: forcing dirstate update\n"))
|
2007-07-22 01:02:10 +04:00
|
|
|
update = True
|
2009-01-12 11:07:02 +03:00
|
|
|
else:
|
2010-03-14 02:36:10 +03:00
|
|
|
parents = [p.node() for p in repo[None].parents()]
|
2009-01-12 11:07:02 +03:00
|
|
|
needupdate = False
|
|
|
|
for entry in self.applied[start:]:
|
2010-03-14 02:36:10 +03:00
|
|
|
if entry.node in parents:
|
2009-01-12 11:07:02 +03:00
|
|
|
needupdate = True
|
|
|
|
break
|
|
|
|
update = needupdate
|
2007-07-22 01:02:10 +04:00
|
|
|
|
2012-05-11 18:17:02 +04:00
|
|
|
tobackup = set()
|
|
|
|
if update:
|
2014-10-05 07:53:05 +04:00
|
|
|
s = self.checklocalchanges(repo, force=force or keepchanges)
|
2012-05-12 02:19:30 +04:00
|
|
|
if force:
|
|
|
|
if not nobackup:
|
2014-10-05 07:53:05 +04:00
|
|
|
tobackup.update(s.modified + s.added)
|
2012-05-13 16:00:58 +04:00
|
|
|
elif keepchanges:
|
2014-10-05 07:53:05 +04:00
|
|
|
tobackup.update(s.modified + s.added +
|
|
|
|
s.removed + s.deleted)
|
2011-06-25 00:25:42 +04:00
|
|
|
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2007-07-22 01:02:10 +04:00
|
|
|
end = len(self.applied)
|
2010-03-14 02:36:10 +03:00
|
|
|
rev = self.applied[start].node
|
2008-02-04 02:03:46 +03:00
|
|
|
|
2009-01-12 11:07:02 +03:00
|
|
|
try:
|
|
|
|
heads = repo.changelog.heads(rev)
|
2009-01-12 19:59:08 +03:00
|
|
|
except error.LookupError:
|
2009-01-12 11:07:02 +03:00
|
|
|
node = short(rev)
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('trying to pop unknown node %s') % node)
|
2009-01-12 11:07:02 +03:00
|
|
|
|
2010-03-14 02:36:10 +03:00
|
|
|
if heads != [self.applied[-1].node]:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("popping would remove a revision not "
|
2008-08-31 18:12:03 +04:00
|
|
|
"managed by this patch queue"))
|
2012-02-01 15:20:10 +04:00
|
|
|
if not repo[self.applied[-1].node].mutable():
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(
|
2015-06-01 21:05:38 +03:00
|
|
|
_("popping would remove a public revision"),
|
2016-09-21 02:44:59 +03:00
|
|
|
hint=_("see 'hg help phases' for details"))
|
2008-02-04 02:03:46 +03:00
|
|
|
|
2007-07-22 01:02:10 +04:00
|
|
|
# we know there are no local changes, so we can make a simplified
|
|
|
|
# form of hg.update.
|
|
|
|
if update:
|
|
|
|
qp = self.qparents(repo, rev)
|
2010-03-13 14:57:20 +03:00
|
|
|
ctx = repo[qp]
|
2013-01-15 04:05:12 +04:00
|
|
|
m, a, r, d = repo.status(qp, '.')[:4]
|
2007-07-22 01:02:10 +04:00
|
|
|
if d:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("deletions found between repo revs"))
|
2012-05-11 18:17:02 +04:00
|
|
|
|
2012-05-12 02:19:30 +04:00
|
|
|
tobackup = set(a + m + r) & tobackup
|
2012-05-13 16:00:58 +04:00
|
|
|
if keepchanges and tobackup:
|
2015-10-14 10:30:27 +03:00
|
|
|
raise error.Abort(_("local changes found, qrefresh first"))
|
2012-05-12 02:19:30 +04:00
|
|
|
self.backup(repo, tobackup)
|
2017-05-19 00:10:53 +03:00
|
|
|
with repo.dirstate.parentchange():
|
|
|
|
for f in a:
|
|
|
|
repo.wvfs.unlinkpath(f, ignoremissing=True)
|
|
|
|
repo.dirstate.drop(f)
|
|
|
|
for f in m + r:
|
|
|
|
fctx = ctx[f]
|
|
|
|
repo.wwrite(f, fctx.data(), fctx.flags())
|
|
|
|
repo.dirstate.normal(f)
|
|
|
|
repo.setparents(qp, nullid)
|
2009-07-10 01:59:03 +04:00
|
|
|
for patch in reversed(self.applied[start:end]):
|
2009-07-11 01:24:35 +04:00
|
|
|
self.ui.status(_("popping %s\n") % patch.name)
|
2007-07-22 01:02:10 +04:00
|
|
|
del self.applied[start:end]
|
2014-07-24 23:06:08 +04:00
|
|
|
strip(self.ui, repo, [rev], update=False, backup=False)
|
2013-08-13 03:38:30 +04:00
|
|
|
for s, state in repo['.'].substate.items():
|
|
|
|
repo['.'].sub(s).get(state)
|
2010-03-14 16:45:01 +03:00
|
|
|
if self.applied:
|
2009-01-03 19:15:21 +03:00
|
|
|
self.ui.write(_("now at: %s\n") % self.applied[-1].name)
|
2006-07-27 20:27:39 +04:00
|
|
|
else:
|
2009-01-03 19:15:21 +03:00
|
|
|
self.ui.write(_("patch queue now empty\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2006-08-17 06:51:39 +04:00
|
|
|
def diff(self, repo, pats, opts):
|
2011-06-13 14:03:38 +04:00
|
|
|
top, patch = self.checktoppatch(repo)
|
2006-02-28 21:25:26 +03:00
|
|
|
if not top:
|
2009-01-03 19:15:21 +03:00
|
|
|
self.ui.write(_("no patches applied\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return
|
|
|
|
qp = self.qparents(repo, top)
|
2009-11-14 16:21:53 +03:00
|
|
|
if opts.get('reverse'):
|
2009-11-05 17:18:56 +03:00
|
|
|
node1, node2 = None, qp
|
|
|
|
else:
|
|
|
|
node1, node2 = qp, None
|
2010-01-01 23:49:05 +03:00
|
|
|
diffopts = self.diffopts(opts, patch)
|
2010-01-01 21:53:05 +03:00
|
|
|
self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2006-08-17 06:52:21 +04:00
|
|
|
def refresh(self, repo, pats=None, **opts):
|
2010-03-14 16:45:01 +03:00
|
|
|
if not self.applied:
|
2009-01-03 19:15:21 +03:00
|
|
|
self.ui.write(_("no patches applied\n"))
|
2006-08-23 02:14:35 +04:00
|
|
|
return 1
|
2008-11-23 01:04:42 +03:00
|
|
|
msg = opts.get('msg', '').rstrip()
|
2014-05-10 19:49:36 +04:00
|
|
|
edit = opts.get('edit')
|
2014-08-02 16:46:26 +04:00
|
|
|
editform = opts.get('editform', 'mq.qrefresh')
|
2008-11-23 01:04:42 +03:00
|
|
|
newuser = opts.get('user')
|
2008-02-17 23:34:28 +03:00
|
|
|
newdate = opts.get('date')
|
|
|
|
if newdate:
|
|
|
|
newdate = '%d %d' % util.parsedate(newdate)
|
2006-02-28 21:25:26 +03:00
|
|
|
wlock = repo.wlock()
|
2010-02-06 20:36:27 +03:00
|
|
|
|
2007-07-22 01:02:10 +04:00
|
|
|
try:
|
2011-06-13 14:03:38 +04:00
|
|
|
self.checktoppatch(repo)
|
2010-03-14 02:36:10 +03:00
|
|
|
(top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
|
2008-02-04 02:03:46 +03:00
|
|
|
if repo.changelog.heads(top) != [top]:
|
2015-10-14 10:30:27 +03:00
|
|
|
raise error.Abort(_("cannot qrefresh a revision with children"))
|
2012-02-01 15:20:10 +04:00
|
|
|
if not repo[top].mutable():
|
2015-10-14 10:30:27 +03:00
|
|
|
raise error.Abort(_("cannot qrefresh public revision"),
|
2016-09-21 02:44:59 +03:00
|
|
|
hint=_("see 'hg help phases' for details"))
|
2010-02-06 20:36:27 +03:00
|
|
|
|
2012-06-27 17:03:27 +04:00
|
|
|
cparents = repo.changelog.parents(top)
|
|
|
|
patchparent = self.qparents(repo, top)
|
|
|
|
|
2013-09-25 13:24:43 +04:00
|
|
|
inclsubs = checksubstate(repo, hex(patchparent))
|
2012-06-27 17:03:27 +04:00
|
|
|
if inclsubs:
|
|
|
|
substatestate = repo.dirstate['.hgsubstate']
|
2010-12-08 07:14:43 +03:00
|
|
|
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(self.join(patchfn), self.plainmode)
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
|
2008-01-02 18:24:13 +03:00
|
|
|
if newuser:
|
2008-11-23 01:04:42 +03:00
|
|
|
ph.setuser(newuser)
|
2008-01-02 18:24:13 +03:00
|
|
|
if newdate:
|
2008-11-23 01:04:42 +03:00
|
|
|
ph.setdate(newdate)
|
2010-02-07 18:47:54 +03:00
|
|
|
ph.setparent(hex(patchparent))
|
2008-01-02 18:24:13 +03:00
|
|
|
|
2008-11-24 03:07:47 +03:00
|
|
|
# only commit new patch when write is complete
|
|
|
|
patchf = self.opener(patchfn, 'w', atomictemp=True)
|
2007-08-16 03:22:12 +04:00
|
|
|
|
2010-02-06 20:36:27 +03:00
|
|
|
# update the dirstate in place, strip off the qtip commit
|
|
|
|
# and then commit.
|
|
|
|
#
|
|
|
|
# this should really read:
|
2010-11-17 11:06:38 +03:00
|
|
|
# mm, dd, aa = repo.status(top, patchparent)[:3]
|
2012-08-16 00:39:18 +04:00
|
|
|
# but we do it backwards to take advantage of manifest/changelog
|
2010-02-06 20:36:27 +03:00
|
|
|
# caching against the next repo.status call
|
2010-11-16 22:06:04 +03:00
|
|
|
mm, aa, dd = repo.status(patchparent, top)[:3]
|
2010-02-06 20:36:27 +03:00
|
|
|
changes = repo.changelog.read(top)
|
2016-11-10 13:13:19 +03:00
|
|
|
man = repo.manifestlog[changes[0]].read()
|
2010-02-06 20:36:27 +03:00
|
|
|
aaa = aa[:]
|
2011-06-19 01:52:51 +04:00
|
|
|
matchfn = scmutil.match(repo[None], pats, opts)
|
2010-02-06 20:36:27 +03:00
|
|
|
# in short mode, we only diff the files included in the
|
|
|
|
# patch already plus specified files
|
|
|
|
if opts.get('short'):
|
|
|
|
# if amending a patch, we start with existing
|
|
|
|
# files plus specified files - unfiltered
|
2011-05-13 23:58:24 +04:00
|
|
|
match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
|
2012-08-16 00:38:42 +04:00
|
|
|
# filter with include/exclude options
|
2011-06-19 01:52:51 +04:00
|
|
|
matchfn = scmutil.match(repo[None], opts=opts)
|
2010-02-06 20:36:27 +03:00
|
|
|
else:
|
2011-05-13 23:58:24 +04:00
|
|
|
match = scmutil.matchall(repo)
|
2010-02-06 20:36:27 +03:00
|
|
|
m, a, r, d = repo.status(match=match)[:4]
|
2010-11-07 16:03:28 +03:00
|
|
|
mm = set(mm)
|
|
|
|
aa = set(aa)
|
|
|
|
dd = set(dd)
|
2010-02-06 20:36:27 +03:00
|
|
|
|
|
|
|
# we might end up with files that were added between
|
|
|
|
# qtip and the dirstate parent, but then changed in the
|
|
|
|
# local dirstate. in this case, we want them to only
|
|
|
|
# show up in the added section
|
|
|
|
for x in m:
|
|
|
|
if x not in aa:
|
2010-11-07 16:03:28 +03:00
|
|
|
mm.add(x)
|
2010-02-06 20:36:27 +03:00
|
|
|
# we might end up with files added by the local dirstate that
|
|
|
|
# were deleted by the patch. In this case, they should only
|
|
|
|
# show up in the changed section.
|
|
|
|
for x in a:
|
|
|
|
if x in dd:
|
2010-11-07 16:03:28 +03:00
|
|
|
dd.remove(x)
|
|
|
|
mm.add(x)
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
2010-11-07 16:03:28 +03:00
|
|
|
aa.add(x)
|
2010-02-06 20:36:27 +03:00
|
|
|
# make sure any files deleted in the local dirstate
|
|
|
|
# are not in the add or change column of the patch
|
|
|
|
forget = []
|
|
|
|
for x in d + r:
|
|
|
|
if x in aa:
|
2010-11-07 16:03:28 +03:00
|
|
|
aa.remove(x)
|
2010-02-06 20:36:27 +03:00
|
|
|
forget.append(x)
|
|
|
|
continue
|
2010-11-07 16:03:28 +03:00
|
|
|
else:
|
|
|
|
mm.discard(x)
|
|
|
|
dd.add(x)
|
2010-02-06 20:36:27 +03:00
|
|
|
|
2010-11-07 16:03:28 +03:00
|
|
|
m = list(mm)
|
|
|
|
r = list(dd)
|
|
|
|
a = list(aa)
|
2012-10-31 00:19:06 +04:00
|
|
|
|
2013-02-10 21:24:29 +04:00
|
|
|
# create 'match' that includes the files to be recommitted.
|
2012-10-31 00:19:06 +04:00
|
|
|
# apply matchfn via repo.status to ensure correct case handling.
|
|
|
|
cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
|
|
|
|
allmatches = set(cm + ca + cr + cd)
|
|
|
|
refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
|
|
|
|
|
|
|
|
files = set(inclsubs)
|
|
|
|
for x in refreshchanges:
|
localrepo: omit ".hgsubstate" also from "added" files
Before this patch, "localrepository.commit()" omits ".hgsubstate" from
"modified" (changes[0]) and "removed" (changes[2]) file list before
checking subrepositories, but leaves one in "added" (changes[1]) as it
is.
Then, "localrepository.commit()" adds ".hgsubstate" into "modified" or
"removed" list forcibly, according to subrepository statuses.
If "added" contains ".hgsubstate", the committed context will contain
two ".hgsubstate" in its "files": one from "added" (not omitted one),
and another from "modified" or "removed" (newly added one).
How many times ".hgsubstate" appears in "files" changes node hash,
even though revision content is same, because node hash calculation
uses the specified "files" directly (without duplication check or so).
This means that node hash of committed revision changes according to
existence of ".hgsubstate" in "added" at "localrepository.commit()".
".hgsubstate" is treated as "added", not only in accidental cases, but
also in the case of "qpush" for the patch adding ".hgsubstate".
This patch omits ".hgsubstate" also from "added" files before checking
subrepositories. This patch also omits ".hgsubstate" exclusion in
"qnew"/"qrefresh" introduced by changeset bbb8109a634f, because this
patch makes them meaningless.
"hg parents --template '{files}\n'" newly added to "test-mq-subrepo.t"
enhances checking unexpected multiple appearances of ".hgsubstate" in
"files" of created/refreshed MQ revisions.
2014-03-22 18:39:51 +04:00
|
|
|
files.update(x)
|
2012-10-31 00:19:06 +04:00
|
|
|
match = scmutil.matchfiles(repo, files)
|
|
|
|
|
2012-10-01 04:44:46 +04:00
|
|
|
bmlist = repo[top].bookmarks()
|
2007-07-22 01:02:10 +04:00
|
|
|
|
2015-05-07 06:07:11 +03:00
|
|
|
dsguard = None
|
2010-02-06 20:36:27 +03:00
|
|
|
try:
|
2016-11-22 05:05:52 +03:00
|
|
|
dsguard = dirstateguard.dirstateguard(repo, 'mq.refresh')
|
2010-02-07 16:43:21 +03:00
|
|
|
if diffopts.git or diffopts.upgrade:
|
2010-02-06 20:36:27 +03:00
|
|
|
copies = {}
|
|
|
|
for dst in a:
|
|
|
|
src = repo.dirstate.copied(dst)
|
|
|
|
# during qfold, the source file for copies may
|
|
|
|
# be removed. Treat this as a simple add.
|
|
|
|
if src is not None and src in repo.dirstate:
|
|
|
|
copies.setdefault(src, []).append(dst)
|
|
|
|
repo.dirstate.add(dst)
|
|
|
|
# remember the copies between patchparent and qtip
|
|
|
|
for dst in aaa:
|
|
|
|
f = repo.file(dst)
|
|
|
|
src = f.renamed(man[dst])
|
|
|
|
if src:
|
2010-02-07 16:43:21 +03:00
|
|
|
copies.setdefault(src[0], []).extend(
|
|
|
|
copies.get(dst, []))
|
2010-02-06 20:36:27 +03:00
|
|
|
if dst in a:
|
|
|
|
copies[src[0]].append(dst)
|
|
|
|
# we can't copy a file created by the patch itself
|
|
|
|
if dst in copies:
|
|
|
|
del copies[dst]
|
|
|
|
for src, dsts in copies.iteritems():
|
|
|
|
for dst in dsts:
|
|
|
|
repo.dirstate.copy(src, dst)
|
|
|
|
else:
|
|
|
|
for dst in a:
|
|
|
|
repo.dirstate.add(dst)
|
|
|
|
# Drop useless copy information
|
|
|
|
for f in list(repo.dirstate.copies()):
|
|
|
|
repo.dirstate.copy(None, f)
|
|
|
|
for f in r:
|
|
|
|
repo.dirstate.remove(f)
|
|
|
|
# if the patch excludes a modified file, mark that
|
|
|
|
# file with mtime=0 so status can see it.
|
|
|
|
mm = []
|
2012-12-10 02:33:16 +04:00
|
|
|
for i in xrange(len(m) - 1, -1, -1):
|
2010-02-06 20:36:27 +03:00
|
|
|
if not matchfn(m[i]):
|
|
|
|
mm.append(m[i])
|
|
|
|
del m[i]
|
|
|
|
for f in m:
|
|
|
|
repo.dirstate.normal(f)
|
|
|
|
for f in mm:
|
|
|
|
repo.dirstate.normallookup(f)
|
|
|
|
for f in forget:
|
2011-05-27 02:15:35 +04:00
|
|
|
repo.dirstate.drop(f)
|
2010-02-06 20:36:27 +03:00
|
|
|
|
|
|
|
user = ph.user or changes[1]
|
2007-12-20 00:36:18 +03:00
|
|
|
|
2012-01-30 20:36:30 +04:00
|
|
|
oldphase = repo[top].phase()
|
|
|
|
|
2010-02-06 20:36:27 +03:00
|
|
|
# assumes strip can roll itself back if interrupted
|
2012-04-30 00:25:55 +04:00
|
|
|
repo.setparents(*cparents)
|
2010-02-06 20:36:27 +03:00
|
|
|
self.applied.pop()
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2014-07-24 23:06:08 +04:00
|
|
|
strip(self.ui, repo, [top], update=False, backup=False)
|
2015-05-07 06:07:11 +03:00
|
|
|
dsguard.close()
|
|
|
|
finally:
|
|
|
|
release(dsguard)
|
2008-11-24 05:08:19 +03:00
|
|
|
|
2010-02-06 20:36:27 +03:00
|
|
|
try:
|
|
|
|
# might be nice to attempt to roll back strip after this
|
2012-02-07 21:47:13 +04:00
|
|
|
|
2014-05-05 16:26:40 +04:00
|
|
|
defaultmsg = "[mq]: %s" % patchfn
|
2014-08-02 16:46:26 +04:00
|
|
|
editor = cmdutil.getcommiteditor(editform=editform)
|
2014-05-10 19:49:36 +04:00
|
|
|
if edit:
|
2014-05-10 19:49:36 +04:00
|
|
|
def finishdesc(desc):
|
2014-05-05 16:26:40 +04:00
|
|
|
if desc.rstrip():
|
|
|
|
ph.setmessage(desc)
|
|
|
|
return desc
|
|
|
|
return defaultmsg
|
2014-05-10 19:49:36 +04:00
|
|
|
# i18n: this message is shown in editor with "HG: " prefix
|
|
|
|
extramsg = _('Leave message empty to use default message.')
|
|
|
|
editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
|
2014-08-02 16:46:26 +04:00
|
|
|
extramsg=extramsg,
|
|
|
|
editform=editform)
|
2014-05-05 16:26:40 +04:00
|
|
|
message = msg or "\n".join(ph.message)
|
|
|
|
elif not msg:
|
2014-05-05 16:26:40 +04:00
|
|
|
if not ph.message:
|
2014-05-05 16:26:40 +04:00
|
|
|
message = defaultmsg
|
2014-05-05 16:26:40 +04:00
|
|
|
else:
|
|
|
|
message = "\n".join(ph.message)
|
|
|
|
else:
|
|
|
|
message = msg
|
|
|
|
ph.setmessage(msg)
|
|
|
|
|
2012-02-07 21:47:13 +04:00
|
|
|
# Ensure we create a new changeset in the same phase than
|
|
|
|
# the old one.
|
2015-11-18 00:12:33 +03:00
|
|
|
lock = tr = None
|
|
|
|
try:
|
|
|
|
lock = repo.lock()
|
|
|
|
tr = repo.transaction('mq')
|
2015-11-18 00:12:46 +03:00
|
|
|
n = newcommit(repo, oldphase, message, user, ph.date,
|
2014-05-05 16:26:40 +04:00
|
|
|
match=match, force=True, editor=editor)
|
2015-11-18 00:12:46 +03:00
|
|
|
# only write patch after a successful commit
|
|
|
|
c = [list(x) for x in refreshchanges]
|
|
|
|
if inclsubs:
|
|
|
|
self.putsubstate2changes(substatestate, c)
|
|
|
|
chunks = patchmod.diff(repo, patchparent,
|
|
|
|
changes=c, opts=diffopts)
|
|
|
|
comments = str(ph)
|
|
|
|
if comments:
|
|
|
|
patchf.write(comments)
|
|
|
|
for chunk in chunks:
|
|
|
|
patchf.write(chunk)
|
|
|
|
patchf.close()
|
|
|
|
|
|
|
|
marks = repo._bookmarks
|
2017-07-10 18:44:25 +03:00
|
|
|
marks.applychanges(repo, tr, [(bm, n) for bm in bmlist])
|
2015-11-18 00:12:33 +03:00
|
|
|
tr.close()
|
2012-10-01 04:44:46 +04:00
|
|
|
|
2015-11-18 00:12:46 +03:00
|
|
|
self.applied.append(statusentry(n, patchfn))
|
2015-11-18 00:12:33 +03:00
|
|
|
finally:
|
2016-08-11 16:05:17 +03:00
|
|
|
lockmod.release(tr, lock)
|
2012-05-13 15:18:06 +04:00
|
|
|
except: # re-raises
|
2010-02-06 20:36:27 +03:00
|
|
|
ctx = repo[cparents[0]]
|
|
|
|
repo.dirstate.rebuild(ctx.node(), ctx.manifest())
|
2011-06-13 14:02:14 +04:00
|
|
|
self.savedirty()
|
2015-10-14 10:30:27 +03:00
|
|
|
self.ui.warn(_('qrefresh interrupted while patch was popped! '
|
2010-02-06 20:36:27 +03:00
|
|
|
'(revert --all, qpush to recover)\n'))
|
|
|
|
raise
|
2007-07-22 01:02:10 +04:00
|
|
|
finally:
|
2009-04-22 04:01:22 +04:00
|
|
|
wlock.release()
|
2008-11-24 05:08:19 +03:00
|
|
|
self.removeundo(repo)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
def init(self, repo, create=False):
|
2007-02-09 08:48:30 +03:00
|
|
|
if not create and os.path.isdir(self.path):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch queue directory already exists"))
|
2007-02-09 08:48:30 +03:00
|
|
|
try:
|
|
|
|
os.mkdir(self.path)
|
2015-06-24 08:20:08 +03:00
|
|
|
except OSError as inst:
|
2007-02-09 08:48:30 +03:00
|
|
|
if inst.errno != errno.EEXIST or not create:
|
|
|
|
raise
|
2006-02-28 21:25:26 +03:00
|
|
|
if create:
|
|
|
|
return self.qrepo(create=True)
|
|
|
|
|
|
|
|
def unapplied(self, repo, patch=None):
|
|
|
|
if patch and patch not in self.series:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch %s is not in series file") % patch)
|
2006-02-28 21:25:26 +03:00
|
|
|
if not patch:
|
2011-06-13 14:11:49 +04:00
|
|
|
start = self.seriesend()
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
|
|
|
start = self.series.index(patch) + 1
|
2006-08-09 08:42:50 +04:00
|
|
|
unapplied = []
|
|
|
|
for i in xrange(start, len(self.series)):
|
|
|
|
pushable, reason = self.pushable(i)
|
|
|
|
if pushable:
|
|
|
|
unapplied.append((i, self.series[i]))
|
2011-06-13 14:00:56 +04:00
|
|
|
self.explainpushable(i)
|
2006-08-09 08:42:50 +04:00
|
|
|
return unapplied
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2007-03-18 14:20:15 +03:00
|
|
|
def qseries(self, repo, missing=None, start=0, length=None, status=None,
|
2006-09-28 00:34:20 +04:00
|
|
|
summary=False):
|
2010-04-03 00:22:14 +04:00
|
|
|
def displayname(pfx, patchname, state):
|
2010-04-15 20:12:28 +04:00
|
|
|
if pfx:
|
|
|
|
self.ui.write(pfx)
|
2006-09-28 00:34:20 +04:00
|
|
|
if summary:
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(self.join(patchname), self.plainmode)
|
2015-03-14 00:00:06 +03:00
|
|
|
if ph.message:
|
|
|
|
msg = ph.message[0]
|
|
|
|
else:
|
|
|
|
msg = ''
|
|
|
|
|
2010-06-07 17:33:03 +04:00
|
|
|
if self.ui.formatted():
|
2010-10-10 19:06:36 +04:00
|
|
|
width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
|
2009-11-18 00:16:41 +03:00
|
|
|
if width > 0:
|
|
|
|
msg = util.ellipsis(msg, width)
|
|
|
|
else:
|
|
|
|
msg = ''
|
2010-04-15 20:12:28 +04:00
|
|
|
self.ui.write(patchname, label='qseries.' + state)
|
|
|
|
self.ui.write(': ')
|
|
|
|
self.ui.write(msg, label='qseries.message.' + state)
|
2006-09-28 00:34:20 +04:00
|
|
|
else:
|
2010-04-15 20:12:28 +04:00
|
|
|
self.ui.write(patchname, label='qseries.' + state)
|
|
|
|
self.ui.write('\n')
|
2006-09-28 00:34:20 +04:00
|
|
|
|
2009-04-22 02:57:28 +04:00
|
|
|
applied = set([p.name for p in self.applied])
|
2007-03-18 14:20:15 +03:00
|
|
|
if length is None:
|
2006-09-28 00:34:20 +04:00
|
|
|
length = len(self.series) - start
|
2006-02-28 21:25:26 +03:00
|
|
|
if not missing:
|
2009-06-27 17:28:44 +04:00
|
|
|
if self.ui.verbose:
|
2010-01-25 09:05:27 +03:00
|
|
|
idxwidth = len(str(start + length - 1))
|
|
|
|
for i in xrange(start, start + length):
|
2007-03-18 14:20:15 +03:00
|
|
|
patch = self.series[i]
|
|
|
|
if patch in applied:
|
2010-04-03 00:22:14 +04:00
|
|
|
char, state = 'A', 'applied'
|
2007-03-18 14:20:15 +03:00
|
|
|
elif self.pushable(i)[0]:
|
2010-04-03 00:22:14 +04:00
|
|
|
char, state = 'U', 'unapplied'
|
2007-03-18 14:20:15 +03:00
|
|
|
else:
|
2010-04-03 00:22:14 +04:00
|
|
|
char, state = 'G', 'guarded'
|
2006-09-28 00:34:20 +04:00
|
|
|
pfx = ''
|
2006-02-28 21:25:26 +03:00
|
|
|
if self.ui.verbose:
|
2010-04-03 00:22:14 +04:00
|
|
|
pfx = '%*d %s ' % (idxwidth, i, char)
|
|
|
|
elif status and status != char:
|
2007-03-17 20:46:52 +03:00
|
|
|
continue
|
2010-04-03 00:22:14 +04:00
|
|
|
displayname(pfx, patch, state)
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
2006-08-06 19:18:53 +04:00
|
|
|
msng_list = []
|
2006-02-28 21:25:26 +03:00
|
|
|
for root, dirs, files in os.walk(self.path):
|
|
|
|
d = root[len(self.path) + 1:]
|
|
|
|
for f in files:
|
|
|
|
fl = os.path.join(d, f)
|
2006-03-08 00:41:24 +03:00
|
|
|
if (fl not in self.series and
|
2011-06-13 15:39:14 +04:00
|
|
|
fl not in (self.statuspath, self.seriespath,
|
2011-06-13 15:40:35 +04:00
|
|
|
self.guardspath)
|
2006-03-08 00:41:24 +03:00
|
|
|
and not fl.startswith('.')):
|
2006-08-06 19:18:53 +04:00
|
|
|
msng_list.append(fl)
|
2009-04-27 01:50:44 +04:00
|
|
|
for x in sorted(msng_list):
|
2006-09-28 00:34:20 +04:00
|
|
|
pfx = self.ui.verbose and ('D ') or ''
|
2010-04-03 00:22:14 +04:00
|
|
|
displayname(pfx, x, 'missing')
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
def issaveline(self, l):
|
2006-08-08 01:48:18 +04:00
|
|
|
if l.name == '.hg.patches.save.line':
|
2006-02-28 21:25:26 +03:00
|
|
|
return True
|
|
|
|
|
|
|
|
def qrepo(self, create=False):
|
2013-04-19 01:53:39 +04:00
|
|
|
ui = self.baseui.copy()
|
2006-08-09 05:12:48 +04:00
|
|
|
if create or os.path.isdir(self.join(".hg")):
|
2010-08-18 04:43:45 +04:00
|
|
|
return hg.repository(ui, path=self.path, create=create)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
def restore(self, repo, rev, delete=None, qupdate=None):
|
2010-03-14 03:06:58 +03:00
|
|
|
desc = repo[rev].description().strip()
|
2006-02-28 21:25:26 +03:00
|
|
|
lines = desc.splitlines()
|
|
|
|
i = 0
|
|
|
|
datastart = None
|
|
|
|
series = []
|
|
|
|
applied = []
|
|
|
|
qpp = None
|
2009-05-27 00:59:52 +04:00
|
|
|
for i, line in enumerate(lines):
|
|
|
|
if line == 'Patch Data:':
|
2006-02-28 21:25:26 +03:00
|
|
|
datastart = i + 1
|
2009-05-27 00:59:52 +04:00
|
|
|
elif line.startswith('Dirstate:'):
|
|
|
|
l = line.rstrip()
|
2006-02-28 21:25:26 +03:00
|
|
|
l = l[10:].split(' ')
|
2010-01-25 09:05:27 +03:00
|
|
|
qpp = [bin(x) for x in l]
|
2010-11-22 20:15:58 +03:00
|
|
|
elif datastart is not None:
|
2009-05-27 00:59:52 +04:00
|
|
|
l = line.rstrip()
|
2010-03-14 13:12:07 +03:00
|
|
|
n, name = l.split(':', 1)
|
|
|
|
if n:
|
2010-03-14 03:35:54 +03:00
|
|
|
applied.append(statusentry(bin(n), name))
|
2010-03-14 13:12:07 +03:00
|
|
|
else:
|
|
|
|
series.append(l)
|
2009-05-20 02:52:46 +04:00
|
|
|
if datastart is None:
|
2012-06-12 16:18:18 +04:00
|
|
|
self.ui.warn(_("no saved patch data found\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 1
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("restoring status: %s\n") % lines[0])
|
2011-06-13 13:06:11 +04:00
|
|
|
self.fullseries = series
|
2006-02-28 21:25:26 +03:00
|
|
|
self.applied = applied
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2012-01-11 05:28:36 +04:00
|
|
|
self.seriesdirty = True
|
|
|
|
self.applieddirty = True
|
2006-02-28 21:25:26 +03:00
|
|
|
heads = repo.changelog.heads()
|
|
|
|
if delete:
|
|
|
|
if rev not in heads:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("save entry has children, leaving it alone\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("removing save entry %s\n") % short(rev))
|
2006-02-28 21:25:26 +03:00
|
|
|
pp = repo.dirstate.parents()
|
|
|
|
if rev in pp:
|
|
|
|
update = True
|
|
|
|
else:
|
|
|
|
update = False
|
2014-07-24 23:06:08 +04:00
|
|
|
strip(self.ui, repo, [rev], update=update, backup=False)
|
2006-02-28 21:25:26 +03:00
|
|
|
if qpp:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("saved queue repository parents: %s %s\n") %
|
2008-03-07 00:51:16 +03:00
|
|
|
(short(qpp[0]), short(qpp[1])))
|
2006-02-28 21:25:26 +03:00
|
|
|
if qupdate:
|
2010-10-26 15:41:58 +04:00
|
|
|
self.ui.status(_("updating queue directory\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
r = self.qrepo()
|
|
|
|
if not r:
|
2012-06-12 16:18:18 +04:00
|
|
|
self.ui.warn(_("unable to load queue repository\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 1
|
2006-08-08 07:54:33 +04:00
|
|
|
hg.clean(r, qpp[0])
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
def save(self, repo, msg=None):
|
2010-03-14 16:45:01 +03:00
|
|
|
if not self.applied:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("save: no patches applied, exiting\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 1
|
|
|
|
if self.issaveline(self.applied[-1]):
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("status is already saved\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 1
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
if not msg:
|
2008-08-31 18:12:03 +04:00
|
|
|
msg = _("hg patches saved state")
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
|
|
|
msg = "hg patches: " + msg.rstrip('\r\n')
|
|
|
|
r = self.qrepo()
|
|
|
|
if r:
|
|
|
|
pp = r.dirstate.parents()
|
2008-03-07 00:51:16 +03:00
|
|
|
msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
|
2006-02-28 21:25:26 +03:00
|
|
|
msg += "\n\nPatch Data:\n"
|
2010-03-14 02:53:55 +03:00
|
|
|
msg += ''.join('%s\n' % x for x in self.applied)
|
2011-06-13 13:06:11 +04:00
|
|
|
msg += ''.join(':%s\n' % x for x in self.fullseries)
|
2010-03-14 02:53:55 +03:00
|
|
|
n = repo.commit(msg, force=True)
|
2006-02-28 21:25:26 +03:00
|
|
|
if not n:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_("repo commit failed\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 1
|
2010-03-14 13:12:51 +03:00
|
|
|
self.applied.append(statusentry(n, '.hg.patches.save.line'))
|
2012-01-11 05:28:36 +04:00
|
|
|
self.applieddirty = True
|
2007-03-14 09:26:09 +03:00
|
|
|
self.removeundo(repo)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-06-13 14:09:29 +04:00
|
|
|
def fullseriesend(self):
|
2010-03-14 16:45:01 +03:00
|
|
|
if self.applied:
|
2006-08-03 22:09:12 +04:00
|
|
|
p = self.applied[-1].name
|
2011-06-13 13:54:25 +04:00
|
|
|
end = self.findseries(p)
|
2009-05-20 02:52:46 +04:00
|
|
|
if end is None:
|
2011-06-13 13:06:11 +04:00
|
|
|
return len(self.fullseries)
|
2006-07-27 20:27:42 +04:00
|
|
|
return end + 1
|
|
|
|
return 0
|
|
|
|
|
2011-06-13 14:11:49 +04:00
|
|
|
def seriesend(self, all_patches=False):
|
2007-05-07 01:44:18 +04:00
|
|
|
"""If all_patches is False, return the index of the next pushable patch
|
|
|
|
in the series, or the series length. If all_patches is True, return the
|
|
|
|
index of the first patch past the last applied one.
|
|
|
|
"""
|
2006-02-28 21:25:26 +03:00
|
|
|
end = 0
|
2013-07-25 18:42:36 +04:00
|
|
|
def nextpatch(start):
|
2010-03-14 16:53:20 +03:00
|
|
|
if all_patches or start >= len(self.series):
|
2006-08-09 08:42:50 +04:00
|
|
|
return start
|
2010-03-14 16:53:20 +03:00
|
|
|
for i in xrange(start, len(self.series)):
|
2006-08-09 08:42:50 +04:00
|
|
|
p, reason = self.pushable(i)
|
|
|
|
if p:
|
2012-02-03 22:38:31 +04:00
|
|
|
return i
|
2011-06-13 14:00:56 +04:00
|
|
|
self.explainpushable(i)
|
2012-02-03 22:38:31 +04:00
|
|
|
return len(self.series)
|
2010-03-14 16:45:01 +03:00
|
|
|
if self.applied:
|
2006-08-03 22:09:12 +04:00
|
|
|
p = self.applied[-1].name
|
2006-02-28 21:25:26 +03:00
|
|
|
try:
|
|
|
|
end = self.series.index(p)
|
|
|
|
except ValueError:
|
|
|
|
return 0
|
2013-07-25 18:42:36 +04:00
|
|
|
return nextpatch(end + 1)
|
|
|
|
return nextpatch(end)
|
2006-02-28 21:25:26 +03:00
|
|
|
|
|
|
|
def appliedname(self, index):
|
2006-08-03 22:09:12 +04:00
|
|
|
pname = self.applied[index].name
|
2006-02-28 21:25:26 +03:00
|
|
|
if not self.ui.verbose:
|
2006-07-26 03:03:33 +04:00
|
|
|
p = pname
|
|
|
|
else:
|
2006-08-17 06:55:42 +04:00
|
|
|
p = str(self.series.index(pname)) + " " + pname
|
2006-02-28 21:25:26 +03:00
|
|
|
return p
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2006-09-19 21:22:30 +04:00
|
|
|
def qimport(self, repo, files, patchname=None, rev=None, existing=None,
|
2006-11-21 00:03:52 +03:00
|
|
|
force=None, git=False):
|
2006-09-19 21:22:30 +04:00
|
|
|
def checkseries(patchname):
|
|
|
|
if patchname in self.series:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('patch %s is already in the series file')
|
2006-09-19 21:22:30 +04:00
|
|
|
% patchname)
|
|
|
|
|
|
|
|
if rev:
|
|
|
|
if files:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('option "-r" not valid when importing '
|
2006-09-19 21:22:30 +04:00
|
|
|
'files'))
|
2011-05-13 23:06:28 +04:00
|
|
|
rev = scmutil.revrange(repo, rev)
|
2009-07-05 13:02:00 +04:00
|
|
|
rev.sort(reverse=True)
|
2012-06-20 18:22:58 +04:00
|
|
|
elif not files:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no files or revisions specified'))
|
2006-09-19 21:22:30 +04:00
|
|
|
if (len(files) > 1 or len(rev) > 1) and patchname:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('option "-n" not valid when importing multiple '
|
2006-09-19 21:22:30 +04:00
|
|
|
'patches'))
|
2012-02-14 17:31:40 +04:00
|
|
|
imported = []
|
2006-09-19 21:22:30 +04:00
|
|
|
if rev:
|
|
|
|
# If mq patches are applied, we can only import revisions
|
|
|
|
# that form a linear path to qbase.
|
|
|
|
# Otherwise, they should form a linear path to a head.
|
2014-10-07 11:33:47 +04:00
|
|
|
heads = repo.changelog.heads(repo.changelog.node(rev.first()))
|
2006-09-19 21:22:30 +04:00
|
|
|
if len(heads) > 1:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d is the root of more than one '
|
2014-10-07 11:14:53 +04:00
|
|
|
'branch') % rev.last())
|
2006-09-19 21:22:30 +04:00
|
|
|
if self.applied:
|
2014-10-07 11:33:47 +04:00
|
|
|
base = repo.changelog.node(rev.first())
|
2010-03-14 02:36:10 +03:00
|
|
|
if base in [n.node for n in self.applied]:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d is already managed')
|
2014-11-02 01:59:37 +03:00
|
|
|
% rev.first())
|
2010-03-14 02:36:10 +03:00
|
|
|
if heads != [self.applied[-1].node]:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d is not the parent of '
|
2014-10-07 11:33:47 +04:00
|
|
|
'the queue') % rev.first())
|
2010-03-14 02:36:10 +03:00
|
|
|
base = repo.changelog.rev(self.applied[0].node)
|
2006-09-19 21:22:30 +04:00
|
|
|
lastparent = repo.changelog.parentrevs(base)[0]
|
|
|
|
else:
|
2014-10-07 11:33:47 +04:00
|
|
|
if heads != [repo.changelog.node(rev.first())]:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d has unmanaged children')
|
2014-10-07 11:33:47 +04:00
|
|
|
% rev.first())
|
2006-09-19 21:22:30 +04:00
|
|
|
lastparent = None
|
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = self.diffopts({'git': git})
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.transaction('qimport') as tr:
|
2014-08-06 11:50:07 +04:00
|
|
|
for r in rev:
|
|
|
|
if not repo[r].mutable():
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d is not mutable') % r,
|
2016-09-21 02:44:59 +03:00
|
|
|
hint=_("see 'hg help phases' "
|
2014-08-06 11:50:07 +04:00
|
|
|
'for details'))
|
|
|
|
p1, p2 = repo.changelog.parentrevs(r)
|
|
|
|
n = repo.changelog.node(r)
|
|
|
|
if p2 != nullrev:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot import merge revision %d')
|
2014-08-06 11:50:07 +04:00
|
|
|
% r)
|
|
|
|
if lastparent and lastparent != r:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('revision %d is not the parent of '
|
2014-08-06 11:50:07 +04:00
|
|
|
'%d')
|
|
|
|
% (r, lastparent))
|
|
|
|
lastparent = p1
|
|
|
|
|
|
|
|
if not patchname:
|
2016-01-19 08:00:30 +03:00
|
|
|
patchname = self.makepatchname(
|
2015-12-23 16:28:52 +03:00
|
|
|
repo[r].description().split('\n', 1)[0],
|
|
|
|
'%d.diff' % r)
|
2014-08-06 11:50:07 +04:00
|
|
|
checkseries(patchname)
|
|
|
|
self.checkpatchname(patchname, force)
|
|
|
|
self.fullseries.insert(0, patchname)
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2014-08-06 11:50:07 +04:00
|
|
|
patchf = self.opener(patchname, "w")
|
|
|
|
cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
|
|
|
|
patchf.close()
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2014-08-06 11:50:07 +04:00
|
|
|
se = statusentry(n, patchname)
|
|
|
|
self.applied.insert(0, se)
|
|
|
|
|
|
|
|
self.added.append(patchname)
|
|
|
|
imported.append(patchname)
|
|
|
|
patchname = None
|
|
|
|
if rev and repo.ui.configbool('mq', 'secret', False):
|
|
|
|
# if we added anything with --rev, move the secret root
|
2014-08-06 10:52:21 +04:00
|
|
|
phases.retractboundary(repo, tr, phases.secret, [n])
|
2014-08-06 11:50:07 +04:00
|
|
|
self.parseseries()
|
|
|
|
self.applieddirty = True
|
|
|
|
self.seriesdirty = True
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2010-03-14 16:53:20 +03:00
|
|
|
for i, filename in enumerate(files):
|
2006-02-28 21:25:26 +03:00
|
|
|
if existing:
|
2006-10-26 20:40:20 +04:00
|
|
|
if filename == '-':
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('-e is incompatible with import from -')
|
|
|
|
)
|
2010-07-21 06:53:09 +04:00
|
|
|
filename = normname(filename)
|
2011-06-13 14:07:46 +04:00
|
|
|
self.checkreservedname(filename)
|
2014-02-10 03:30:49 +04:00
|
|
|
if util.url(filename).islocal():
|
2014-02-08 03:54:10 +04:00
|
|
|
originpath = self.join(filename)
|
|
|
|
if not os.path.isfile(originpath):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(
|
2014-02-10 03:30:49 +04:00
|
|
|
_("patch %s does not exist") % filename)
|
2010-07-21 06:53:09 +04:00
|
|
|
|
|
|
|
if patchname:
|
2011-05-24 20:16:51 +04:00
|
|
|
self.checkpatchname(patchname, force)
|
2010-07-21 06:53:09 +04:00
|
|
|
|
|
|
|
self.ui.write(_('renaming %s to %s\n')
|
|
|
|
% (filename, patchname))
|
2010-07-28 13:07:20 +04:00
|
|
|
util.rename(originpath, self.join(patchname))
|
2010-07-21 06:53:09 +04:00
|
|
|
else:
|
|
|
|
patchname = filename
|
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
2011-05-20 22:19:33 +04:00
|
|
|
if filename == '-' and not patchname:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('need --name to import a patch from -'))
|
2011-05-20 22:19:33 +04:00
|
|
|
elif not patchname:
|
2011-05-20 22:20:24 +04:00
|
|
|
patchname = normname(os.path.basename(filename.rstrip('/')))
|
2011-05-24 20:16:51 +04:00
|
|
|
self.checkpatchname(patchname, force)
|
2006-02-28 21:25:26 +03:00
|
|
|
try:
|
2006-10-26 20:40:20 +04:00
|
|
|
if filename == '-':
|
2011-06-08 15:54:52 +04:00
|
|
|
text = self.ui.fin.read()
|
2006-10-26 20:40:20 +04:00
|
|
|
else:
|
2012-10-18 08:30:08 +04:00
|
|
|
fp = hg.openpath(self.ui, filename)
|
2010-12-24 17:23:01 +03:00
|
|
|
text = fp.read()
|
|
|
|
fp.close()
|
2008-11-26 20:23:35 +03:00
|
|
|
except (OSError, IOError):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("unable to read file %s") % filename)
|
2006-09-19 19:45:39 +04:00
|
|
|
patchf = self.opener(patchname, "w")
|
2006-02-28 21:25:26 +03:00
|
|
|
patchf.write(text)
|
2010-12-24 17:23:01 +03:00
|
|
|
patchf.close()
|
2008-10-19 04:29:45 +04:00
|
|
|
if not force:
|
|
|
|
checkseries(patchname)
|
|
|
|
if patchname not in self.series:
|
2011-06-13 14:09:29 +04:00
|
|
|
index = self.fullseriesend() + i
|
2011-06-13 13:06:11 +04:00
|
|
|
self.fullseries[index:index] = [patchname]
|
2011-06-13 13:55:46 +04:00
|
|
|
self.parseseries()
|
2011-06-13 15:45:48 +04:00
|
|
|
self.seriesdirty = True
|
2009-01-03 18:15:11 +03:00
|
|
|
self.ui.warn(_("adding %s to series file\n") % patchname)
|
2010-06-29 01:39:42 +04:00
|
|
|
self.added.append(patchname)
|
2012-02-14 17:31:40 +04:00
|
|
|
imported.append(patchname)
|
2006-09-19 19:45:39 +04:00
|
|
|
patchname = None
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-02-15 22:02:52 +03:00
|
|
|
self.removeundo(repo)
|
2012-02-14 17:31:40 +04:00
|
|
|
return imported
|
2011-02-15 22:02:52 +03:00
|
|
|
|
2012-05-13 16:00:58 +04:00
|
|
|
def fixkeepchangesopts(ui, opts):
|
|
|
|
if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
|
2012-05-12 02:19:30 +04:00
|
|
|
or opts.get('exact')):
|
|
|
|
return opts
|
|
|
|
opts = dict(opts)
|
2012-05-13 16:00:58 +04:00
|
|
|
opts['keep_changes'] = True
|
2012-05-12 02:19:30 +04:00
|
|
|
return opts
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qdelete|qremove|qrm",
|
|
|
|
[('k', 'keep', None, _('keep patch file')),
|
|
|
|
('r', 'rev', [],
|
|
|
|
_('stop managing a revision (DEPRECATED)'), _('REV'))],
|
|
|
|
_('hg qdelete [-k] [PATCH]...'))
|
2006-10-13 00:24:09 +04:00
|
|
|
def delete(ui, repo, *patches, **opts):
|
2006-08-14 22:41:08 +04:00
|
|
|
"""remove patches from queue
|
2006-08-01 23:21:55 +04:00
|
|
|
|
2012-01-06 18:05:51 +04:00
|
|
|
The patches must not be applied, and at least one patch is required. Exact
|
|
|
|
patch identifiers must be given. With -k/--keep, the patch files are
|
|
|
|
preserved in the patch directory.
|
2009-06-24 00:20:25 +04:00
|
|
|
|
|
|
|
To stop managing a patch and move it into permanent history,
|
2010-06-08 16:12:46 +04:00
|
|
|
use the :hg:`qfinish` command."""
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2006-10-13 00:24:09 +04:00
|
|
|
q.delete(repo, patches, opts)
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qapplied",
|
2012-02-27 21:23:18 +04:00
|
|
|
[('1', 'last', None, _('show only the preceding applied patch'))
|
2011-05-12 16:25:08 +04:00
|
|
|
] + seriesopts,
|
|
|
|
_('hg qapplied [-1] [-s] [PATCH]'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def applied(ui, repo, patch=None, **opts):
|
2010-09-23 18:56:32 +04:00
|
|
|
"""print the patches already applied
|
|
|
|
|
|
|
|
Returns 0 on success."""
|
2009-08-19 14:50:46 +04:00
|
|
|
|
2006-09-28 00:34:20 +04:00
|
|
|
q = repo.mq
|
2009-08-19 14:50:46 +04:00
|
|
|
|
2006-09-28 00:34:20 +04:00
|
|
|
if patch:
|
|
|
|
if patch not in q.series:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch %s is not in series file") % patch)
|
2006-09-28 00:34:20 +04:00
|
|
|
end = q.series.index(patch) + 1
|
|
|
|
else:
|
2011-06-13 14:11:49 +04:00
|
|
|
end = q.seriesend(True)
|
2009-08-19 14:50:46 +04:00
|
|
|
|
|
|
|
if opts.get('last') and not end:
|
|
|
|
ui.write(_("no patches applied\n"))
|
|
|
|
return 1
|
|
|
|
elif opts.get('last') and end == 1:
|
|
|
|
ui.write(_("only one patch applied\n"))
|
|
|
|
return 1
|
|
|
|
elif opts.get('last'):
|
|
|
|
start = end - 2
|
|
|
|
end = 1
|
|
|
|
else:
|
|
|
|
start = 0
|
|
|
|
|
2010-09-23 17:18:10 +04:00
|
|
|
q.qseries(repo, length=end, start=start, status='A',
|
|
|
|
summary=opts.get('summary'))
|
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qunapplied",
|
|
|
|
[('1', 'first', None, _('show only the first patch'))] + seriesopts,
|
|
|
|
_('hg qunapplied [-1] [-s] [PATCH]'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def unapplied(ui, repo, patch=None, **opts):
|
2010-09-23 18:56:32 +04:00
|
|
|
"""print the patches not yet applied
|
|
|
|
|
|
|
|
Returns 0 on success."""
|
2009-08-19 14:50:46 +04:00
|
|
|
|
2006-09-28 00:34:20 +04:00
|
|
|
q = repo.mq
|
|
|
|
if patch:
|
|
|
|
if patch not in q.series:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_("patch %s is not in series file") % patch)
|
2006-09-28 00:34:20 +04:00
|
|
|
start = q.series.index(patch) + 1
|
|
|
|
else:
|
2011-06-13 14:11:49 +04:00
|
|
|
start = q.seriesend(True)
|
2009-08-19 14:50:46 +04:00
|
|
|
|
|
|
|
if start == len(q.series) and opts.get('first'):
|
|
|
|
ui.write(_("all patches applied\n"))
|
|
|
|
return 1
|
|
|
|
|
2015-03-14 00:00:06 +03:00
|
|
|
if opts.get('first'):
|
|
|
|
length = 1
|
|
|
|
else:
|
|
|
|
length = None
|
2010-09-23 17:18:10 +04:00
|
|
|
q.qseries(repo, start=start, length=length, status='U',
|
|
|
|
summary=opts.get('summary'))
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qimport",
|
|
|
|
[('e', 'existing', None, _('import file in patch directory')),
|
|
|
|
('n', 'name', '',
|
|
|
|
_('name of patch file'), _('NAME')),
|
|
|
|
('f', 'force', None, _('overwrite existing files')),
|
|
|
|
('r', 'rev', [],
|
|
|
|
_('place existing revisions under mq control'), _('REV')),
|
|
|
|
('g', 'git', None, _('use git extended diff format')),
|
|
|
|
('P', 'push', None, _('qpush after importing'))],
|
2012-06-20 18:43:09 +04:00
|
|
|
_('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def qimport(ui, repo, *filename, **opts):
|
2012-02-23 03:41:22 +04:00
|
|
|
"""import a patch or existing changeset
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
The patch is inserted into the series after the last applied
|
|
|
|
patch. If no patches have been applied, qimport prepends the patch
|
|
|
|
to the series.
|
2008-06-03 11:31:36 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
The patch will have the same name as its source file unless you
|
|
|
|
give it a new one with -n/--name.
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
You can register an existing patch inside the patch directory with
|
|
|
|
the -e/--existing flag.
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
With -f/--force, an existing patch of the same name will be
|
|
|
|
overwritten.
|
2006-09-19 21:22:30 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
An existing changeset may be placed under mq control with -r/--rev
|
2013-07-12 04:25:44 +04:00
|
|
|
(e.g. qimport --rev . -n patch will place the current revision
|
|
|
|
under mq control). With -g/--git, patches imported with --rev will
|
|
|
|
use the git diff format. See the diffs help topic for information
|
|
|
|
on why this is important for preserving rename/copy information
|
|
|
|
and permission changes. Use :hg:`qfinish` to remove changesets
|
|
|
|
from mq control.
|
2009-04-18 11:21:11 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
To import a patch from standard input, pass - as the patch file.
|
|
|
|
When importing from standard input, a patch name must be specified
|
|
|
|
using the --name flag.
|
2010-07-23 18:43:43 +04:00
|
|
|
|
2010-07-29 12:18:17 +04:00
|
|
|
To import an existing patch while renaming it::
|
2010-07-23 18:43:43 +04:00
|
|
|
|
|
|
|
hg qimport -e existing-patch -n new-name
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 if import succeeded.
|
2006-09-19 21:22:30 +04:00
|
|
|
"""
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.lock(): # cause this may move phase
|
2012-01-30 20:37:45 +04:00
|
|
|
q = repo.mq
|
|
|
|
try:
|
2012-02-14 17:31:40 +04:00
|
|
|
imported = q.qimport(
|
|
|
|
repo, filename, patchname=opts.get('name'),
|
|
|
|
existing=opts.get('existing'), force=opts.get('force'),
|
|
|
|
rev=opts.get('rev'), git=opts.get('git'))
|
2012-01-30 20:37:45 +04:00
|
|
|
finally:
|
|
|
|
q.savedirty()
|
2012-01-30 20:37:45 +04:00
|
|
|
|
|
|
|
if imported and opts.get('push') and not opts.get('rev'):
|
|
|
|
return q.push(repo, imported[-1])
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2010-02-15 11:16:59 +03:00
|
|
|
def qinit(ui, repo, create):
|
|
|
|
"""initialize a new queue repository
|
2006-08-02 02:24:12 +04:00
|
|
|
|
2010-02-15 11:16:59 +03:00
|
|
|
This command also creates a series file for ordering patches, and
|
|
|
|
an mq-specific .hgignore file in the queue repository, to exclude
|
2010-09-23 18:56:32 +04:00
|
|
|
the status and guards files (these contain mostly transient state).
|
|
|
|
|
|
|
|
Returns 0 if initialization succeeded."""
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2010-02-15 11:16:59 +03:00
|
|
|
r = q.init(repo, create)
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
if r:
|
2007-02-09 08:48:30 +03:00
|
|
|
if not os.path.exists(r.wjoin('.hgignore')):
|
2015-01-11 03:51:52 +03:00
|
|
|
fp = r.wvfs('.hgignore', 'w')
|
2008-02-08 23:07:55 +03:00
|
|
|
fp.write('^\\.hg\n')
|
|
|
|
fp.write('^\\.mq\n')
|
2007-02-09 08:48:30 +03:00
|
|
|
fp.write('syntax: glob\n')
|
|
|
|
fp.write('status\n')
|
|
|
|
fp.write('guards\n')
|
|
|
|
fp.close()
|
|
|
|
if not os.path.exists(r.wjoin('series')):
|
2015-01-11 03:51:52 +03:00
|
|
|
r.wvfs('series', 'w').close()
|
2010-06-07 22:03:32 +04:00
|
|
|
r[None].add(['.hgignore', 'series'])
|
2007-02-09 08:48:30 +03:00
|
|
|
commands.add(ui, r)
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("^qinit",
|
|
|
|
[('c', 'create-repo', None, _('create queue repository'))],
|
|
|
|
_('hg qinit [-c]'))
|
2010-02-15 11:16:59 +03:00
|
|
|
def init(ui, repo, **opts):
|
|
|
|
"""init a new queue repository (DEPRECATED)
|
|
|
|
|
|
|
|
The queue repository is unversioned by default. If
|
|
|
|
-c/--create-repo is specified, qinit will create a separate nested
|
|
|
|
repository for patches (qinit -c may also be run later to convert
|
|
|
|
an unversioned patch repository into a versioned one). You can use
|
|
|
|
qcommit to commit changes to this queue repository.
|
|
|
|
|
|
|
|
This command is deprecated. Without -c, it's implied by other relevant
|
2010-05-18 18:31:10 +04:00
|
|
|
commands. With -c, use :hg:`init --mq` instead."""
|
2010-09-15 00:11:46 +04:00
|
|
|
return qinit(ui, repo, create=opts.get('create_repo'))
|
2010-02-15 11:16:59 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qclone",
|
|
|
|
[('', 'pull', None, _('use pull protocol to copy metadata')),
|
2012-05-12 17:54:54 +04:00
|
|
|
('U', 'noupdate', None,
|
|
|
|
_('do not update the new working directories')),
|
2011-05-12 16:25:08 +04:00
|
|
|
('', 'uncompressed', None,
|
|
|
|
_('use uncompressed transfer (fast over LAN)')),
|
|
|
|
('p', 'patches', '',
|
|
|
|
_('location of source patch repository'), _('REPO')),
|
2017-05-14 10:19:47 +03:00
|
|
|
] + cmdutil.remoteopts,
|
2014-05-05 09:04:14 +04:00
|
|
|
_('hg qclone [OPTION]... SOURCE [DEST]'),
|
|
|
|
norepo=True)
|
2006-07-28 21:46:41 +04:00
|
|
|
def clone(ui, source, dest=None, **opts):
|
|
|
|
'''clone main and patch repository at same time
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
If source is local, destination will have no patches applied. If
|
|
|
|
source is remote, this command can not check if patches are
|
|
|
|
applied in source, so cannot guarantee that patches are not
|
|
|
|
applied in destination. If you clone remote repository, be sure
|
|
|
|
before that it has no patches applied.
|
2006-07-28 21:46:41 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Source patch repository is looked for in <src>/.hg/patches by
|
|
|
|
default. Use -p <url> to change.
|
2007-07-10 20:52:32 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
The patch directory must be a nested Mercurial repository, as
|
2010-05-18 18:31:10 +04:00
|
|
|
would be created by :hg:`init --mq`.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Return 0 on success.
|
2006-07-28 21:46:41 +04:00
|
|
|
'''
|
2007-08-27 08:44:35 +04:00
|
|
|
def patchdir(repo):
|
2012-01-18 20:11:27 +04:00
|
|
|
"""compute a patch repo url from a repo object"""
|
2007-08-27 08:44:35 +04:00
|
|
|
url = repo.url()
|
|
|
|
if url.endswith('/'):
|
|
|
|
url = url[:-1]
|
|
|
|
return url + '/.hg/patches'
|
2012-01-18 20:11:27 +04:00
|
|
|
|
|
|
|
# main repo (destination and sources)
|
2006-07-28 21:46:41 +04:00
|
|
|
if dest is None:
|
|
|
|
dest = hg.defaultdest(source)
|
2012-07-13 23:46:53 +04:00
|
|
|
sr = hg.peer(ui, opts, ui.expandpath(source))
|
2012-01-18 20:11:27 +04:00
|
|
|
|
|
|
|
# patches repo (source only)
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('patches'):
|
|
|
|
patchespath = ui.expandpath(opts.get('patches'))
|
2009-01-25 21:49:15 +03:00
|
|
|
else:
|
|
|
|
patchespath = patchdir(sr)
|
2007-07-10 20:52:32 +04:00
|
|
|
try:
|
2012-07-13 23:46:53 +04:00
|
|
|
hg.peer(ui, opts, patchespath)
|
2009-01-12 19:42:31 +03:00
|
|
|
except error.RepoError:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('versioned patch repository not found'
|
2010-03-14 21:21:24 +03:00
|
|
|
' (see init --mq)'))
|
2006-07-28 21:46:41 +04:00
|
|
|
qbase, destrev = None, None
|
|
|
|
if sr.local():
|
2012-07-13 23:46:53 +04:00
|
|
|
repo = sr.local()
|
|
|
|
if repo.mq.applied and repo[qbase].phase() != phases.secret:
|
|
|
|
qbase = repo.mq.applied[0].node
|
2006-07-28 21:46:41 +04:00
|
|
|
if not hg.islocal(dest):
|
2012-07-13 23:46:53 +04:00
|
|
|
heads = set(repo.heads())
|
|
|
|
destrev = list(heads.difference(repo.heads(qbase)))
|
|
|
|
destrev.append(repo.changelog.parents(qbase)[0])
|
2008-02-22 01:00:50 +03:00
|
|
|
elif sr.capable('lookup'):
|
2008-03-24 22:17:19 +03:00
|
|
|
try:
|
|
|
|
qbase = sr.lookup('qbase')
|
2009-01-12 19:42:31 +03:00
|
|
|
except error.RepoError:
|
2008-03-24 22:17:19 +03:00
|
|
|
pass
|
2012-01-18 20:11:27 +04:00
|
|
|
|
2009-04-08 00:58:05 +04:00
|
|
|
ui.note(_('cloning main repository\n'))
|
2011-06-08 20:24:33 +04:00
|
|
|
sr, dr = hg.clone(ui, opts, sr.url(), dest,
|
2010-09-15 00:11:46 +04:00
|
|
|
pull=opts.get('pull'),
|
2006-07-28 21:46:41 +04:00
|
|
|
rev=destrev,
|
|
|
|
update=False,
|
2010-09-15 00:11:46 +04:00
|
|
|
stream=opts.get('uncompressed'))
|
2012-01-18 20:11:27 +04:00
|
|
|
|
2009-04-08 00:58:05 +04:00
|
|
|
ui.note(_('cloning patch repository\n'))
|
2011-06-08 20:24:33 +04:00
|
|
|
hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
|
2010-09-15 00:11:46 +04:00
|
|
|
pull=opts.get('pull'), update=not opts.get('noupdate'),
|
|
|
|
stream=opts.get('uncompressed'))
|
2012-01-18 20:11:27 +04:00
|
|
|
|
2006-07-28 21:46:41 +04:00
|
|
|
if dr.local():
|
2012-07-13 23:46:53 +04:00
|
|
|
repo = dr.local()
|
2006-07-28 21:46:41 +04:00
|
|
|
if qbase:
|
2009-04-08 00:58:05 +04:00
|
|
|
ui.note(_('stripping applied patches from destination '
|
|
|
|
'repository\n'))
|
2013-09-25 16:07:37 +04:00
|
|
|
strip(ui, repo, [qbase], update=False, backup=None)
|
2010-09-15 00:11:46 +04:00
|
|
|
if not opts.get('noupdate'):
|
2009-04-08 00:58:05 +04:00
|
|
|
ui.note(_('updating destination repository\n'))
|
2012-07-13 23:46:53 +04:00
|
|
|
hg.update(repo, repo.changelog.tip())
|
2006-07-28 21:46:41 +04:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qcommit|qci",
|
|
|
|
commands.table["^commit|ci"][1],
|
2014-05-05 09:30:47 +04:00
|
|
|
_('hg qcommit [OPTION]... [FILE]...'),
|
|
|
|
inferrepo=True)
|
2006-02-28 21:25:26 +03:00
|
|
|
def commit(ui, repo, *pats, **opts):
|
2010-02-07 15:49:52 +03:00
|
|
|
"""commit changes in the queue repository (DEPRECATED)
|
|
|
|
|
2010-05-18 18:31:10 +04:00
|
|
|
This command is deprecated; use :hg:`commit --mq` instead."""
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2006-02-28 21:25:26 +03:00
|
|
|
r = q.qrepo()
|
2010-01-25 09:05:27 +03:00
|
|
|
if not r:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort('no queue repository')
|
2006-02-28 21:25:26 +03:00
|
|
|
commands.commit(r.ui, r, *pats, **opts)
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qseries",
|
|
|
|
[('m', 'missing', None, _('print patches not in series')),
|
|
|
|
] + seriesopts,
|
|
|
|
_('hg qseries [-ms]'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def series(ui, repo, **opts):
|
2010-09-23 18:56:32 +04:00
|
|
|
"""print the entire series file
|
|
|
|
|
|
|
|
Returns 0 on success."""
|
2012-05-12 17:54:54 +04:00
|
|
|
repo.mq.qseries(repo, missing=opts.get('missing'),
|
|
|
|
summary=opts.get('summary'))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2011-05-30 22:44:03 +04:00
|
|
|
@command("qtop", seriesopts, _('hg qtop [-s]'))
|
2009-09-15 01:39:24 +04:00
|
|
|
def top(ui, repo, **opts):
|
2010-09-23 18:56:32 +04:00
|
|
|
"""print the name of the current patch
|
|
|
|
|
|
|
|
Returns 0 on success."""
|
2009-09-15 01:39:24 +04:00
|
|
|
q = repo.mq
|
2015-03-14 00:00:06 +03:00
|
|
|
if q.applied:
|
|
|
|
t = q.seriesend(True)
|
|
|
|
else:
|
|
|
|
t = 0
|
|
|
|
|
2009-09-15 01:39:24 +04:00
|
|
|
if t:
|
2010-09-23 17:18:10 +04:00
|
|
|
q.qseries(repo, start=t - 1, length=1, status='A',
|
|
|
|
summary=opts.get('summary'))
|
2009-09-15 01:39:24 +04:00
|
|
|
else:
|
|
|
|
ui.write(_("no patches applied\n"))
|
|
|
|
return 1
|
|
|
|
|
2011-05-30 22:44:03 +04:00
|
|
|
@command("qnext", seriesopts, _('hg qnext [-s]'))
|
2009-09-15 01:39:24 +04:00
|
|
|
def next(ui, repo, **opts):
|
2012-02-03 22:38:31 +04:00
|
|
|
"""print the name of the next pushable patch
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success."""
|
2009-09-15 01:39:24 +04:00
|
|
|
q = repo.mq
|
2011-06-13 14:11:49 +04:00
|
|
|
end = q.seriesend()
|
2009-09-15 01:39:24 +04:00
|
|
|
if end == len(q.series):
|
|
|
|
ui.write(_("all patches applied\n"))
|
|
|
|
return 1
|
2010-09-23 17:18:10 +04:00
|
|
|
q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
|
2009-09-15 01:39:24 +04:00
|
|
|
|
2011-05-30 22:44:03 +04:00
|
|
|
@command("qprev", seriesopts, _('hg qprev [-s]'))
|
2009-09-15 01:39:24 +04:00
|
|
|
def prev(ui, repo, **opts):
|
2012-02-27 21:23:18 +04:00
|
|
|
"""print the name of the preceding applied patch
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success."""
|
2009-09-15 01:39:24 +04:00
|
|
|
q = repo.mq
|
|
|
|
l = len(q.applied)
|
|
|
|
if l == 1:
|
|
|
|
ui.write(_("only one patch applied\n"))
|
|
|
|
return 1
|
|
|
|
if not l:
|
|
|
|
ui.write(_("no patches applied\n"))
|
|
|
|
return 1
|
2012-02-03 22:47:09 +04:00
|
|
|
idx = q.series.index(q.applied[-2].name)
|
|
|
|
q.qseries(repo, start=idx, length=1, status='A',
|
2010-09-23 17:18:10 +04:00
|
|
|
summary=opts.get('summary'))
|
2009-09-15 01:39:24 +04:00
|
|
|
|
2007-12-20 00:36:18 +03:00
|
|
|
def setupheaderopts(ui, opts):
|
2009-11-07 02:13:05 +03:00
|
|
|
if not opts.get('user') and opts.get('currentuser'):
|
|
|
|
opts['user'] = ui.username()
|
|
|
|
if not opts.get('date') and opts.get('currentdate'):
|
|
|
|
opts['date'] = "%d %d" % util.makedate()
|
2007-12-20 00:36:18 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("^qnew",
|
2014-07-29 19:14:52 +04:00
|
|
|
[('e', 'edit', None, _('invoke editor on commit messages')),
|
2011-05-12 16:25:08 +04:00
|
|
|
('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
|
|
|
|
('g', 'git', None, _('use git extended diff format')),
|
|
|
|
('U', 'currentuser', None, _('add "From: <current user>" to patch')),
|
|
|
|
('u', 'user', '',
|
|
|
|
_('add "From: <USER>" to patch'), _('USER')),
|
|
|
|
('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
|
|
|
|
('d', 'date', '',
|
|
|
|
_('add "Date: <DATE>" to patch'), _('DATE'))
|
2017-05-14 10:19:47 +03:00
|
|
|
] + cmdutil.walkopts + cmdutil.commitopts,
|
2014-05-05 09:30:47 +04:00
|
|
|
_('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
|
|
|
|
inferrepo=True)
|
2007-06-25 22:13:27 +04:00
|
|
|
def new(ui, repo, patch, *args, **opts):
|
2006-08-02 02:24:12 +04:00
|
|
|
"""create a new patch
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
qnew creates a new patch on top of the currently-applied patch (if
|
2010-04-02 02:32:08 +04:00
|
|
|
any). The patch will be initialized with any outstanding changes
|
|
|
|
in the working directory. You may also use -I/--include,
|
2009-07-26 03:53:49 +04:00
|
|
|
-X/--exclude, and/or a list of files after the patch name to add
|
|
|
|
only changes to matching files to the new patch, leaving the rest
|
|
|
|
as uncommitted modifications.
|
|
|
|
|
|
|
|
-u/--user and -d/--date can be used to set the (given) user and
|
|
|
|
date, respectively. -U/--currentuser and -D/--currentdate set user
|
|
|
|
to current user and date to current date.
|
|
|
|
|
|
|
|
-e/--edit, -m/--message or -l/--logfile set the patch header as
|
|
|
|
well as the commit message. If none is specified, the header is
|
|
|
|
empty and the commit message is '[mq]: PATCH'.
|
|
|
|
|
|
|
|
Use the -g/--git option to keep the patch in the git extended diff
|
|
|
|
format. Read the diffs help topic for more information on why this
|
|
|
|
is important for preserving permission changes and copy/rename
|
|
|
|
information.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on successful creation of a new patch.
|
2008-11-03 18:30:21 +03:00
|
|
|
"""
|
2011-06-08 15:54:52 +04:00
|
|
|
msg = cmdutil.logmessage(ui, opts)
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2008-10-19 03:33:10 +04:00
|
|
|
opts['msg'] = msg
|
2007-12-20 00:36:18 +03:00
|
|
|
setupheaderopts(ui, opts)
|
2007-06-25 22:13:27 +04:00
|
|
|
q.new(repo, patch, *args, **opts)
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("^qrefresh",
|
2014-07-29 19:14:52 +04:00
|
|
|
[('e', 'edit', None, _('invoke editor on commit messages')),
|
2011-05-12 16:25:08 +04:00
|
|
|
('g', 'git', None, _('use git extended diff format')),
|
|
|
|
('s', 'short', None,
|
|
|
|
_('refresh only files already in the patch and specified files')),
|
|
|
|
('U', 'currentuser', None,
|
|
|
|
_('add/update author field in patch with current user')),
|
|
|
|
('u', 'user', '',
|
|
|
|
_('add/update author field in patch with given user'), _('USER')),
|
|
|
|
('D', 'currentdate', None,
|
|
|
|
_('add/update date field in patch with current date')),
|
|
|
|
('d', 'date', '',
|
|
|
|
_('add/update date field in patch with given date'), _('DATE'))
|
2017-05-14 10:19:47 +03:00
|
|
|
] + cmdutil.walkopts + cmdutil.commitopts,
|
2014-05-05 09:30:47 +04:00
|
|
|
_('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
|
|
|
|
inferrepo=True)
|
2006-08-17 06:52:21 +04:00
|
|
|
def refresh(ui, repo, *pats, **opts):
|
2006-08-17 06:53:42 +04:00
|
|
|
"""update the current patch
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
If any file patterns are provided, the refreshed patch will
|
|
|
|
contain only the modifications that match those patterns; the
|
|
|
|
remaining modifications will remain in the working directory.
|
2007-01-29 19:33:09 +03:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
If -s/--short is specified, files currently included in the patch
|
|
|
|
will be refreshed just like matched files and remain in the patch.
|
2008-10-17 23:26:39 +04:00
|
|
|
|
2010-08-16 23:35:20 +04:00
|
|
|
If -e/--edit is specified, Mercurial will start your configured editor for
|
|
|
|
you to enter a message. In case qrefresh fails, you will find a backup of
|
|
|
|
your message in ``.hg/last-message.txt``.
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
hg add/remove/copy/rename work as usual, though you might want to
|
|
|
|
use git-style patches (-g/--git or [diff] git=1) to track copies
|
|
|
|
and renames. See the diffs help topic for more information on the
|
|
|
|
git diff format.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success.
|
2006-08-17 06:53:42 +04:00
|
|
|
"""
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2011-06-08 15:54:52 +04:00
|
|
|
message = cmdutil.logmessage(ui, opts)
|
2007-12-20 00:36:18 +03:00
|
|
|
setupheaderopts(ui, opts)
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.wlock():
|
2014-05-10 19:49:36 +04:00
|
|
|
ret = q.refresh(repo, pats, msg=message, **opts)
|
2011-06-14 20:50:49 +04:00
|
|
|
q.savedirty()
|
|
|
|
return ret
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("^qdiff",
|
2017-05-14 10:19:47 +03:00
|
|
|
cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
|
2014-05-05 09:30:47 +04:00
|
|
|
_('hg qdiff [OPTION]... [FILE]...'),
|
|
|
|
inferrepo=True)
|
2006-08-17 06:51:39 +04:00
|
|
|
def diff(ui, repo, *pats, **opts):
|
2008-05-17 11:11:14 +04:00
|
|
|
"""diff of the current patch and subsequent modifications
|
2008-05-22 15:28:34 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Shows a diff which includes the current patch as well as any
|
|
|
|
changes which have been made in the working directory since the
|
|
|
|
last refresh (thus showing what the current patch would become
|
|
|
|
after a qrefresh).
|
2008-05-22 15:28:34 +04:00
|
|
|
|
2010-04-22 12:24:49 +04:00
|
|
|
Use :hg:`diff` if you only want to see the changes made since the
|
|
|
|
last qrefresh, or :hg:`export qtip` if you want to see changes
|
|
|
|
made by the current patch without including changes made since the
|
2009-07-26 03:53:49 +04:00
|
|
|
qrefresh.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success.
|
2008-05-17 11:11:14 +04:00
|
|
|
"""
|
2017-02-07 07:57:21 +03:00
|
|
|
ui.pager('qdiff')
|
2006-08-17 06:51:39 +04:00
|
|
|
repo.mq.diff(repo, pats, opts)
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command('qfold',
|
2014-07-29 19:14:52 +04:00
|
|
|
[('e', 'edit', None, _('invoke editor on commit messages')),
|
2011-05-12 16:25:08 +04:00
|
|
|
('k', 'keep', None, _('keep folded patch files')),
|
2017-05-14 10:19:47 +03:00
|
|
|
] + cmdutil.commitopts,
|
2011-05-12 16:25:08 +04:00
|
|
|
_('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
|
2006-08-02 02:24:11 +04:00
|
|
|
def fold(ui, repo, *files, **opts):
|
2006-08-01 07:33:56 +04:00
|
|
|
"""fold the named patches into the current patch
|
2006-08-02 02:24:11 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Patches must not yet be applied. Each patch will be successively
|
|
|
|
applied to the current patch in the order given. If all the
|
|
|
|
patches apply successfully, the current patch will be refreshed
|
|
|
|
with the new cumulative patch, and the folded patches will be
|
|
|
|
deleted. With -k/--keep, the folded patch files will not be
|
|
|
|
removed afterwards.
|
2006-08-03 02:07:37 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
The header for each folded patch will be concatenated with the
|
2010-10-18 16:37:50 +04:00
|
|
|
current patch header, separated by a line of ``* * *``.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success."""
|
2006-08-01 07:33:56 +04:00
|
|
|
q = repo.mq
|
|
|
|
if not files:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('qfold requires at least one patch name'))
|
2011-06-13 14:03:38 +04:00
|
|
|
if not q.checktoppatch(repo)[0]:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no patches applied'))
|
2011-06-13 14:06:32 +04:00
|
|
|
q.checklocalchanges(repo)
|
2006-08-01 07:33:56 +04:00
|
|
|
|
2011-06-08 15:54:52 +04:00
|
|
|
message = cmdutil.logmessage(ui, opts)
|
2006-08-02 02:24:11 +04:00
|
|
|
|
2006-08-01 07:33:56 +04:00
|
|
|
parent = q.lookup('qtip')
|
|
|
|
patches = []
|
|
|
|
messages = []
|
|
|
|
for f in files:
|
2006-08-17 06:50:50 +04:00
|
|
|
p = q.lookup(f)
|
|
|
|
if p in patches or p == parent:
|
2012-06-12 16:18:18 +04:00
|
|
|
ui.warn(_('skipping already folded patch %s\n') % p)
|
2006-08-17 06:50:50 +04:00
|
|
|
if q.isapplied(p):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('qfold cannot fold already applied patch %s')
|
2012-05-12 17:54:54 +04:00
|
|
|
% p)
|
2006-08-17 06:50:50 +04:00
|
|
|
patches.append(p)
|
|
|
|
|
|
|
|
for p in patches:
|
2006-08-02 02:24:11 +04:00
|
|
|
if not message:
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(q.join(p), q.plainmode)
|
2008-12-02 07:10:02 +03:00
|
|
|
if ph.message:
|
|
|
|
messages.append(ph.message)
|
2006-08-17 06:50:50 +04:00
|
|
|
pf = q.join(p)
|
2006-08-01 07:33:56 +04:00
|
|
|
(patchsuccess, files, fuzz) = q.patch(repo, pf)
|
|
|
|
if not patchsuccess:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('error folding patch %s') % p)
|
2006-08-01 07:33:56 +04:00
|
|
|
|
2006-08-02 02:24:11 +04:00
|
|
|
if not message:
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(q.join(parent), q.plainmode)
|
2014-05-09 03:44:53 +04:00
|
|
|
message = ph.message
|
2006-08-02 02:24:11 +04:00
|
|
|
for msg in messages:
|
2013-11-17 00:46:28 +04:00
|
|
|
if msg:
|
|
|
|
if message:
|
|
|
|
message.append('* * *')
|
|
|
|
message.extend(msg)
|
2006-08-02 02:24:11 +04:00
|
|
|
message = '\n'.join(message)
|
|
|
|
|
2010-01-01 21:53:05 +03:00
|
|
|
diffopts = q.patchopts(q.diffopts(), *patches)
|
2016-01-16 00:14:47 +03:00
|
|
|
with repo.wlock():
|
2014-08-02 16:46:26 +04:00
|
|
|
q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
|
|
|
|
editform='mq.qfold')
|
2011-06-14 20:50:49 +04:00
|
|
|
q.delete(repo, patches, opts)
|
|
|
|
q.savedirty()
|
2006-08-01 07:33:56 +04:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qgoto",
|
2012-05-13 16:00:58 +04:00
|
|
|
[('', 'keep-changes', None,
|
|
|
|
_('tolerate non-conflicting local changes')),
|
2012-05-12 02:19:30 +04:00
|
|
|
('f', 'force', None, _('overwrite any local changes')),
|
2012-05-11 18:57:26 +04:00
|
|
|
('', 'no-backup', None, _('do not save backup copies of files'))],
|
2011-05-12 16:25:08 +04:00
|
|
|
_('hg qgoto [OPTION]... PATCH'))
|
2007-05-09 00:10:27 +04:00
|
|
|
def goto(ui, repo, patch, **opts):
|
2010-09-23 18:56:32 +04:00
|
|
|
'''push or pop patches until named patch is at top of stack
|
|
|
|
|
|
|
|
Returns 0 on success.'''
|
2012-05-13 16:00:58 +04:00
|
|
|
opts = fixkeepchangesopts(ui, opts)
|
2007-05-09 00:10:27 +04:00
|
|
|
q = repo.mq
|
|
|
|
patch = q.lookup(patch)
|
2012-05-11 18:57:26 +04:00
|
|
|
nobackup = opts.get('no_backup')
|
2012-05-13 16:00:58 +04:00
|
|
|
keepchanges = opts.get('keep_changes')
|
2007-05-09 00:10:27 +04:00
|
|
|
if q.isapplied(patch):
|
2012-05-12 02:19:30 +04:00
|
|
|
ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
|
2012-05-13 16:00:58 +04:00
|
|
|
keepchanges=keepchanges)
|
2007-05-09 00:10:27 +04:00
|
|
|
else:
|
2012-05-12 02:19:30 +04:00
|
|
|
ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
|
2012-05-13 16:00:58 +04:00
|
|
|
keepchanges=keepchanges)
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2007-05-09 00:10:27 +04:00
|
|
|
return ret
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qguard",
|
|
|
|
[('l', 'list', None, _('list all patches and guards')),
|
|
|
|
('n', 'none', None, _('drop all guards'))],
|
|
|
|
_('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
|
2006-08-09 08:42:50 +04:00
|
|
|
def guard(ui, repo, *args, **opts):
|
|
|
|
'''set or print guards for a patch
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Guards control whether a patch can be pushed. A patch with no
|
|
|
|
guards is always pushed. A patch with a positive guard ("+foo") is
|
2010-06-08 16:12:46 +04:00
|
|
|
pushed only if the :hg:`qselect` command has activated it. A patch with
|
|
|
|
a negative guard ("-foo") is never pushed if the :hg:`qselect` command
|
2009-07-26 03:53:49 +04:00
|
|
|
has activated it.
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
With no arguments, print the currently active guards.
|
|
|
|
With arguments, set guards for the named patch.
|
2010-09-22 18:23:55 +04:00
|
|
|
|
|
|
|
.. note::
|
2013-11-05 11:59:55 +04:00
|
|
|
|
2010-09-22 18:23:55 +04:00
|
|
|
Specifying negative guards now requires '--'.
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2009-11-11 02:05:12 +03:00
|
|
|
To set guards on another patch::
|
|
|
|
|
2010-02-15 02:12:10 +03:00
|
|
|
hg qguard other.patch -- +2.6.17 -stable
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success.
|
2006-08-09 08:42:50 +04:00
|
|
|
'''
|
|
|
|
def status(idx):
|
2011-06-13 13:40:27 +04:00
|
|
|
guards = q.seriesguards[idx] or ['unguarded']
|
2010-08-05 14:25:53 +04:00
|
|
|
if q.series[idx] in applied:
|
|
|
|
state = 'applied'
|
|
|
|
elif q.pushable(idx)[0]:
|
|
|
|
state = 'unapplied'
|
|
|
|
else:
|
|
|
|
state = 'guarded'
|
|
|
|
label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
|
|
|
|
ui.write('%s: ' % ui.label(q.series[idx], label))
|
|
|
|
|
2010-04-03 00:22:12 +04:00
|
|
|
for i, guard in enumerate(guards):
|
|
|
|
if guard.startswith('+'):
|
2010-06-09 00:52:41 +04:00
|
|
|
ui.write(guard, label='qguard.positive')
|
2010-04-03 00:22:12 +04:00
|
|
|
elif guard.startswith('-'):
|
2010-06-09 00:52:41 +04:00
|
|
|
ui.write(guard, label='qguard.negative')
|
2010-04-03 00:22:12 +04:00
|
|
|
else:
|
2010-06-09 00:52:41 +04:00
|
|
|
ui.write(guard, label='qguard.unguarded')
|
2010-04-03 00:22:12 +04:00
|
|
|
if i != len(guards) - 1:
|
2010-06-09 00:52:41 +04:00
|
|
|
ui.write(' ')
|
|
|
|
ui.write('\n')
|
2006-08-09 08:42:50 +04:00
|
|
|
q = repo.mq
|
2010-08-05 14:25:53 +04:00
|
|
|
applied = set(p.name for p in q.applied)
|
2006-08-09 08:42:50 +04:00
|
|
|
patch = None
|
|
|
|
args = list(args)
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('list'):
|
|
|
|
if args or opts.get('none'):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot mix -l/--list with options or '
|
2012-05-12 17:54:54 +04:00
|
|
|
'arguments'))
|
2006-08-09 08:42:50 +04:00
|
|
|
for i in xrange(len(q.series)):
|
|
|
|
status(i)
|
|
|
|
return
|
|
|
|
if not args or args[0][0:1] in '-+':
|
|
|
|
if not q.applied:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no patches applied'))
|
2006-08-09 08:42:50 +04:00
|
|
|
patch = q.applied[-1].name
|
|
|
|
if patch is None and args[0][0:1] not in '-+':
|
|
|
|
patch = args.pop(0)
|
|
|
|
if patch is None:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no patch to work with'))
|
2010-09-15 00:11:46 +04:00
|
|
|
if args or opts.get('none'):
|
2011-06-13 13:54:25 +04:00
|
|
|
idx = q.findseries(patch)
|
2007-03-03 19:54:13 +03:00
|
|
|
if idx is None:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no patch named %s') % patch)
|
2011-06-13 13:58:16 +04:00
|
|
|
q.setguards(idx, args)
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-08-09 08:42:50 +04:00
|
|
|
else:
|
|
|
|
status(q.series.index(q.lookup(patch)))
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qheader", [], _('hg qheader [PATCH]'))
|
2006-08-01 05:39:31 +04:00
|
|
|
def header(ui, repo, patch=None):
|
2010-09-23 18:56:32 +04:00
|
|
|
"""print the header of the topmost or specified patch
|
|
|
|
|
|
|
|
Returns 0 on success."""
|
2006-08-01 05:39:31 +04:00
|
|
|
q = repo.mq
|
|
|
|
|
|
|
|
if patch:
|
|
|
|
patch = q.lookup(patch)
|
|
|
|
else:
|
|
|
|
if not q.applied:
|
2010-02-19 04:23:38 +03:00
|
|
|
ui.write(_('no patches applied\n'))
|
2006-08-23 03:59:09 +04:00
|
|
|
return 1
|
2006-08-01 05:39:31 +04:00
|
|
|
patch = q.lookup('qtip')
|
2010-02-07 18:47:54 +03:00
|
|
|
ph = patchheader(q.join(patch), q.plainmode)
|
2006-08-01 05:39:31 +04:00
|
|
|
|
2008-11-23 01:04:42 +03:00
|
|
|
ui.write('\n'.join(ph.message) + '\n')
|
2006-08-01 05:39:31 +04:00
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
def lastsavename(path):
|
2006-08-06 19:18:53 +04:00
|
|
|
(directory, base) = os.path.split(path)
|
|
|
|
names = os.listdir(directory)
|
2006-02-28 21:25:26 +03:00
|
|
|
namere = re.compile("%s.([0-9]+)" % base)
|
2006-08-06 19:18:53 +04:00
|
|
|
maxindex = None
|
2006-02-28 21:25:26 +03:00
|
|
|
maxname = None
|
|
|
|
for f in names:
|
|
|
|
m = namere.match(f)
|
|
|
|
if m:
|
|
|
|
index = int(m.group(1))
|
2009-05-20 02:52:46 +04:00
|
|
|
if maxindex is None or index > maxindex:
|
2006-08-06 19:18:53 +04:00
|
|
|
maxindex = index
|
2006-02-28 21:25:26 +03:00
|
|
|
maxname = f
|
|
|
|
if maxname:
|
2006-08-06 19:18:53 +04:00
|
|
|
return (os.path.join(directory, maxname), maxindex)
|
2006-02-28 21:25:26 +03:00
|
|
|
return (None, None)
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
def savename(path):
|
|
|
|
(last, index) = lastsavename(path)
|
|
|
|
if last is None:
|
|
|
|
index = 0
|
|
|
|
newpath = path + ".%d" % (index + 1)
|
|
|
|
return newpath
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("^qpush",
|
2012-05-13 16:00:58 +04:00
|
|
|
[('', 'keep-changes', None,
|
|
|
|
_('tolerate non-conflicting local changes')),
|
2012-05-12 02:19:30 +04:00
|
|
|
('f', 'force', None, _('apply on top of local changes')),
|
2012-05-12 17:54:54 +04:00
|
|
|
('e', 'exact', None,
|
|
|
|
_('apply the target patch to its recorded parent')),
|
2011-05-12 16:25:08 +04:00
|
|
|
('l', 'list', None, _('list patch name in commit text')),
|
|
|
|
('a', 'all', None, _('apply all patches')),
|
|
|
|
('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
|
|
|
|
('n', 'name', '',
|
|
|
|
_('merge queue name (DEPRECATED)'), _('NAME')),
|
2012-05-11 18:57:26 +04:00
|
|
|
('', 'move', None,
|
|
|
|
_('reorder patch series and apply only the patch')),
|
|
|
|
('', 'no-backup', None, _('do not save backup copies of files'))],
|
2011-05-12 16:25:08 +04:00
|
|
|
_('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def push(ui, repo, patch=None, **opts):
|
2008-04-16 11:35:43 +04:00
|
|
|
"""push the next patch onto the stack
|
2008-04-16 14:09:16 +04:00
|
|
|
|
2012-05-12 02:19:30 +04:00
|
|
|
By default, abort if the working directory contains uncommitted
|
2012-05-13 16:00:58 +04:00
|
|
|
changes. With --keep-changes, abort only if the uncommitted files
|
2012-05-12 02:19:30 +04:00
|
|
|
overlap with patched files. With -f/--force, backup and patch over
|
|
|
|
uncommitted changes.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
2011-03-15 16:55:16 +03:00
|
|
|
Return 0 on success.
|
2008-04-16 11:35:43 +04:00
|
|
|
"""
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2006-02-28 21:25:26 +03:00
|
|
|
mergeq = None
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2012-05-13 16:00:58 +04:00
|
|
|
opts = fixkeepchangesopts(ui, opts)
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('merge'):
|
|
|
|
if opts.get('name'):
|
2017-03-09 03:52:16 +03:00
|
|
|
newpath = repo.vfs.join(opts.get('name'))
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
2006-03-01 01:25:34 +03:00
|
|
|
newpath, i = lastsavename(q.path)
|
2006-02-28 21:25:26 +03:00
|
|
|
if not newpath:
|
2008-08-31 18:12:03 +04:00
|
|
|
ui.warn(_("no saved queues found, please use -n\n"))
|
2006-02-28 21:25:26 +03:00
|
|
|
return 1
|
2013-04-19 01:53:39 +04:00
|
|
|
mergeq = queue(ui, repo.baseui, repo.path, newpath)
|
2008-08-31 18:12:03 +04:00
|
|
|
ui.warn(_("merging with queue at: %s\n") % mergeq.path)
|
2010-09-15 00:11:46 +04:00
|
|
|
ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
|
2010-11-18 05:18:44 +03:00
|
|
|
mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
|
2012-05-12 02:19:30 +04:00
|
|
|
exact=opts.get('exact'), nobackup=opts.get('no_backup'),
|
2012-05-13 16:00:58 +04:00
|
|
|
keepchanges=opts.get('keep_changes'))
|
2006-02-28 21:25:26 +03:00
|
|
|
return ret
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("^qpop",
|
|
|
|
[('a', 'all', None, _('pop all patches')),
|
|
|
|
('n', 'name', '',
|
|
|
|
_('queue name to pop (DEPRECATED)'), _('NAME')),
|
2012-05-13 16:00:58 +04:00
|
|
|
('', 'keep-changes', None,
|
|
|
|
_('tolerate non-conflicting local changes')),
|
2012-05-11 18:57:26 +04:00
|
|
|
('f', 'force', None, _('forget any local changes to patched files')),
|
|
|
|
('', 'no-backup', None, _('do not save backup copies of files'))],
|
2011-05-12 16:25:08 +04:00
|
|
|
_('hg qpop [-a] [-f] [PATCH | INDEX]'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def pop(ui, repo, patch=None, **opts):
|
2008-05-19 12:22:15 +04:00
|
|
|
"""pop the current patch off the stack
|
2008-05-22 15:28:34 +04:00
|
|
|
|
2012-05-12 02:19:30 +04:00
|
|
|
Without argument, pops off the top of the patch stack. If given a
|
|
|
|
patch name, keeps popping off patches until the named patch is at
|
|
|
|
the top of the stack.
|
|
|
|
|
|
|
|
By default, abort if the working directory contains uncommitted
|
2012-05-13 16:00:58 +04:00
|
|
|
changes. With --keep-changes, abort only if the uncommitted files
|
2012-05-12 02:19:30 +04:00
|
|
|
overlap with patched files. With -f/--force, backup and discard
|
|
|
|
changes made to such files.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Return 0 on success.
|
2008-05-19 12:22:15 +04:00
|
|
|
"""
|
2012-05-13 16:00:58 +04:00
|
|
|
opts = fixkeepchangesopts(ui, opts)
|
2006-02-28 21:25:26 +03:00
|
|
|
localupdate = True
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('name'):
|
2017-03-09 03:52:16 +03:00
|
|
|
q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
|
2008-08-31 18:12:03 +04:00
|
|
|
ui.warn(_('using patch queue: %s\n') % q.path)
|
2006-02-28 21:25:26 +03:00
|
|
|
localupdate = False
|
|
|
|
else:
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2010-09-15 00:11:46 +04:00
|
|
|
ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
|
2012-05-12 02:19:30 +04:00
|
|
|
all=opts.get('all'), nobackup=opts.get('no_backup'),
|
2012-05-13 16:00:58 +04:00
|
|
|
keepchanges=opts.get('keep_changes'))
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2007-02-17 09:50:03 +03:00
|
|
|
return ret
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
|
2006-08-01 21:55:06 +04:00
|
|
|
def rename(ui, repo, patch, name=None, **opts):
|
|
|
|
"""rename a patch
|
|
|
|
|
|
|
|
With one argument, renames the current patch to PATCH1.
|
2010-09-23 18:56:32 +04:00
|
|
|
With two arguments, renames PATCH1 to PATCH2.
|
|
|
|
|
|
|
|
Returns 0 on success."""
|
2006-08-01 21:55:06 +04:00
|
|
|
q = repo.mq
|
|
|
|
if not name:
|
|
|
|
name = patch
|
|
|
|
patch = None
|
|
|
|
|
|
|
|
if patch:
|
|
|
|
patch = q.lookup(patch)
|
|
|
|
else:
|
|
|
|
if not q.applied:
|
2009-01-03 19:15:21 +03:00
|
|
|
ui.write(_('no patches applied\n'))
|
2006-08-01 21:55:06 +04:00
|
|
|
return
|
|
|
|
patch = q.lookup('qtip')
|
2006-09-13 19:57:08 +04:00
|
|
|
absdest = q.join(name)
|
|
|
|
if os.path.isdir(absdest):
|
2007-01-18 00:19:36 +03:00
|
|
|
name = normname(os.path.join(name, os.path.basename(patch)))
|
2006-09-13 19:57:08 +04:00
|
|
|
absdest = q.join(name)
|
2011-05-24 20:16:51 +04:00
|
|
|
q.checkpatchname(name)
|
2006-08-01 21:55:06 +04:00
|
|
|
|
2010-02-19 04:23:38 +03:00
|
|
|
ui.note(_('renaming %s to %s\n') % (patch, name))
|
2011-06-13 13:54:25 +04:00
|
|
|
i = q.findseries(patch)
|
2011-06-13 13:06:11 +04:00
|
|
|
guards = q.guard_re.findall(q.fullseries[i])
|
|
|
|
q.fullseries[i] = name + ''.join([' #' + g for g in guards])
|
2011-06-13 13:55:46 +04:00
|
|
|
q.parseseries()
|
2012-01-11 05:28:36 +04:00
|
|
|
q.seriesdirty = True
|
2006-08-01 21:55:06 +04:00
|
|
|
|
|
|
|
info = q.isapplied(patch)
|
|
|
|
if info:
|
2006-08-08 01:59:32 +04:00
|
|
|
q.applied[info[0]] = statusentry(info[1], name)
|
2012-01-11 05:28:36 +04:00
|
|
|
q.applieddirty = True
|
2006-08-01 21:55:06 +04:00
|
|
|
|
2010-03-28 10:23:11 +04:00
|
|
|
destdir = os.path.dirname(absdest)
|
|
|
|
if not os.path.isdir(destdir):
|
|
|
|
os.makedirs(destdir)
|
2006-08-09 05:12:48 +04:00
|
|
|
util.rename(q.join(patch), absdest)
|
2006-08-01 21:55:06 +04:00
|
|
|
r = q.qrepo()
|
2010-10-29 00:04:33 +04:00
|
|
|
if r and patch in r.dirstate:
|
2010-06-07 22:03:32 +04:00
|
|
|
wctx = r[None]
|
2016-01-16 00:14:50 +03:00
|
|
|
with r.wlock():
|
2008-06-06 20:23:31 +04:00
|
|
|
if r.dirstate[patch] == 'a':
|
2011-05-27 02:15:35 +04:00
|
|
|
r.dirstate.drop(patch)
|
2008-06-06 20:23:31 +04:00
|
|
|
r.dirstate.add(name)
|
|
|
|
else:
|
2010-06-07 22:03:32 +04:00
|
|
|
wctx.copy(patch, name)
|
2011-05-27 02:15:35 +04:00
|
|
|
wctx.forget([patch])
|
2006-08-01 21:55:06 +04:00
|
|
|
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-08-01 21:55:06 +04:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qrestore",
|
|
|
|
[('d', 'delete', None, _('delete save entry')),
|
|
|
|
('u', 'update', None, _('update queue working directory'))],
|
|
|
|
_('hg qrestore [-d] [-u] REV'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def restore(ui, repo, rev, **opts):
|
2010-02-07 14:34:02 +03:00
|
|
|
"""restore the queue state saved by a revision (DEPRECATED)
|
|
|
|
|
mq: fix the deprecation comment for qsave & qrestore.
Changeset 1e63d4dee4f6 deprecated qsave and qrestore. In the
deprecating comment, users were referred to 'rebase --mq' which -- at
the time the message was written -- didn't exist. Currently, on the
default branch, rebase *does* take a '--mq' option, but it probably
doesn't do what Dirkjan expected it to do when he wrote the message.
In the original, deprecating commit, little context was provided as to
why this change was made. Based on my recollection, concensus at the
Paris Sprint in February 2010 was that one of the problems with MQ was
that it exposed far too many commands. Notable among these were qsave
& qrestore: very few core developers understood what they did and even
fewer (none, IIRC) actually used them. However, they couldn't be
removed; not only do the usual backwards compatibility reasons apply,
but the hg book refers to them.
2010-09-20 15:31:02 +04:00
|
|
|
This command is deprecated, use :hg:`rebase` instead."""
|
2006-02-28 21:25:26 +03:00
|
|
|
rev = repo.lookup(rev)
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2010-09-15 00:11:46 +04:00
|
|
|
q.restore(repo, rev, delete=opts.get('delete'),
|
|
|
|
qupdate=opts.get('update'))
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qsave",
|
|
|
|
[('c', 'copy', None, _('copy patch directory')),
|
|
|
|
('n', 'name', '',
|
|
|
|
_('copy directory name'), _('NAME')),
|
|
|
|
('e', 'empty', None, _('clear queue status file')),
|
2017-05-14 10:19:47 +03:00
|
|
|
('f', 'force', None, _('force copy'))] + cmdutil.commitopts,
|
2011-05-12 16:25:08 +04:00
|
|
|
_('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
|
2006-02-28 21:25:26 +03:00
|
|
|
def save(ui, repo, **opts):
|
2010-02-07 14:34:02 +03:00
|
|
|
"""save current queue state (DEPRECATED)
|
|
|
|
|
mq: fix the deprecation comment for qsave & qrestore.
Changeset 1e63d4dee4f6 deprecated qsave and qrestore. In the
deprecating comment, users were referred to 'rebase --mq' which -- at
the time the message was written -- didn't exist. Currently, on the
default branch, rebase *does* take a '--mq' option, but it probably
doesn't do what Dirkjan expected it to do when he wrote the message.
In the original, deprecating commit, little context was provided as to
why this change was made. Based on my recollection, concensus at the
Paris Sprint in February 2010 was that one of the problems with MQ was
that it exposed far too many commands. Notable among these were qsave
& qrestore: very few core developers understood what they did and even
fewer (none, IIRC) actually used them. However, they couldn't be
removed; not only do the usual backwards compatibility reasons apply,
but the hg book refers to them.
2010-09-20 15:31:02 +04:00
|
|
|
This command is deprecated, use :hg:`rebase` instead."""
|
2006-07-29 00:08:25 +04:00
|
|
|
q = repo.mq
|
2011-06-08 15:54:52 +04:00
|
|
|
message = cmdutil.logmessage(ui, opts)
|
2006-07-26 22:11:54 +04:00
|
|
|
ret = q.save(repo, msg=message)
|
2006-02-28 21:25:26 +03:00
|
|
|
if ret:
|
|
|
|
return ret
|
2012-01-11 05:29:55 +04:00
|
|
|
q.savedirty() # save to .hg/patches before copying
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('copy'):
|
2006-02-28 21:25:26 +03:00
|
|
|
path = q.path
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('name'):
|
|
|
|
newpath = os.path.join(q.basepath, opts.get('name'))
|
2006-02-28 21:25:26 +03:00
|
|
|
if os.path.exists(newpath):
|
|
|
|
if not os.path.isdir(newpath):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('destination %s exists and is not '
|
2006-07-28 03:41:59 +04:00
|
|
|
'a directory') % newpath)
|
2010-09-15 00:11:46 +04:00
|
|
|
if not opts.get('force'):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('destination %s exists, '
|
2006-07-28 03:41:59 +04:00
|
|
|
'use -f to force') % newpath)
|
2006-02-28 21:25:26 +03:00
|
|
|
else:
|
|
|
|
newpath = savename(path)
|
2008-08-31 18:12:03 +04:00
|
|
|
ui.warn(_("copy %s to %s\n") % (path, newpath))
|
2006-02-28 21:25:26 +03:00
|
|
|
util.copyfiles(path, newpath)
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('empty'):
|
2012-01-11 05:29:55 +04:00
|
|
|
del q.applied[:]
|
|
|
|
q.applieddirty = True
|
|
|
|
q.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
return 0
|
2006-03-01 01:25:34 +03:00
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qselect",
|
|
|
|
[('n', 'none', None, _('disable all guards')),
|
|
|
|
('s', 'series', None, _('list all guards in series file')),
|
|
|
|
('', 'pop', None, _('pop to before first guarded applied patch')),
|
|
|
|
('', 'reapply', None, _('pop, then reapply patches'))],
|
|
|
|
_('hg qselect [OPTION]... [GUARD]...'))
|
2006-08-09 08:42:50 +04:00
|
|
|
def select(ui, repo, *args, **opts):
|
|
|
|
'''set or print guarded patches to push
|
|
|
|
|
2010-06-08 16:12:46 +04:00
|
|
|
Use the :hg:`qguard` command to set or print guards on patch, then use
|
2009-07-26 03:53:49 +04:00
|
|
|
qselect to tell mq which guards to use. A patch will be pushed if
|
|
|
|
it has no guards or any positive guards match the currently
|
|
|
|
selected guard, but will not be pushed if any negative guards
|
2009-11-11 02:05:12 +03:00
|
|
|
match the current guard. For example::
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2011-03-29 17:45:23 +04:00
|
|
|
qguard foo.patch -- -stable (negative guard)
|
|
|
|
qguard bar.patch +stable (positive guard)
|
2006-08-09 08:42:50 +04:00
|
|
|
qselect stable
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
This activates the "stable" guard. mq will skip foo.patch (because
|
|
|
|
it has a negative match) but push bar.patch (because it has a
|
|
|
|
positive match).
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
With no arguments, prints the currently active guards.
|
|
|
|
With one argument, sets the active guard.
|
2006-10-01 21:26:33 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Use -n/--none to deactivate guards (no other arguments needed).
|
|
|
|
When no guards are active, patches with positive guards are
|
|
|
|
skipped and patches with negative guards are pushed.
|
2006-08-09 08:42:50 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
qselect can change the guards on applied patches. It does not pop
|
|
|
|
guarded patches by default. Use --pop to pop back to the last
|
|
|
|
applied patch that is not guarded. Use --reapply (which implies
|
|
|
|
--pop) to push back to the current patch afterwards, but skip
|
|
|
|
guarded patches.
|
2006-08-11 01:58:10 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Use -s/--series to print a list of all guards in the series file
|
2010-09-23 18:56:32 +04:00
|
|
|
(no other arguments needed). Use -v for more information.
|
|
|
|
|
|
|
|
Returns 0 on success.'''
|
2006-08-09 08:42:50 +04:00
|
|
|
|
|
|
|
q = repo.mq
|
|
|
|
guards = q.active()
|
mq: report correct numbers for changing "number of guarded, applied patches"
Before this patch, "hg qselect" may report incorrect numbers for
"number of guarded, applied patches has changed", because it examines
"pushable" of patches by the index not in "the series" but in "applied
patches", even though "mq.pushable()" expects the former.
To report correct numbers for changing "number of guarded, applied
patches", this patch uses the name of applied patch to examine
pushable-ness of it.
This patch also changes the result of existing "hg qselect" tests,
because they doesn't change pushable-ness of already applied patches.
This patch assumes that "hg qselect" focuses on changing pushable-ness
only of already applied patches, because:
- the report message uses not "previous" (in the series) but
"applied"
- the logic to pop patches for --pop/--reapply examines
pushable-ness only of already applied ones (in fact, there are
some incorrect code paths)
2014-09-11 21:29:19 +04:00
|
|
|
pushable = lambda i: q.pushable(q.applied[i].name)[0]
|
2010-09-15 00:11:46 +04:00
|
|
|
if args or opts.get('none'):
|
2006-08-11 01:58:10 +04:00
|
|
|
old_unapplied = q.unapplied(repo)
|
mq: report correct numbers for changing "number of guarded, applied patches"
Before this patch, "hg qselect" may report incorrect numbers for
"number of guarded, applied patches has changed", because it examines
"pushable" of patches by the index not in "the series" but in "applied
patches", even though "mq.pushable()" expects the former.
To report correct numbers for changing "number of guarded, applied
patches", this patch uses the name of applied patch to examine
pushable-ness of it.
This patch also changes the result of existing "hg qselect" tests,
because they doesn't change pushable-ness of already applied patches.
This patch assumes that "hg qselect" focuses on changing pushable-ness
only of already applied patches, because:
- the report message uses not "previous" (in the series) but
"applied"
- the logic to pop patches for --pop/--reapply examines
pushable-ness only of already applied ones (in fact, there are
some incorrect code paths)
2014-09-11 21:29:19 +04:00
|
|
|
old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
|
2011-06-13 13:59:35 +04:00
|
|
|
q.setactive(args)
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-08-09 08:42:50 +04:00
|
|
|
if not args:
|
|
|
|
ui.status(_('guards deactivated\n'))
|
2010-09-15 00:11:46 +04:00
|
|
|
if not opts.get('pop') and not opts.get('reapply'):
|
2006-08-11 01:58:10 +04:00
|
|
|
unapplied = q.unapplied(repo)
|
mq: report correct numbers for changing "number of guarded, applied patches"
Before this patch, "hg qselect" may report incorrect numbers for
"number of guarded, applied patches has changed", because it examines
"pushable" of patches by the index not in "the series" but in "applied
patches", even though "mq.pushable()" expects the former.
To report correct numbers for changing "number of guarded, applied
patches", this patch uses the name of applied patch to examine
pushable-ness of it.
This patch also changes the result of existing "hg qselect" tests,
because they doesn't change pushable-ness of already applied patches.
This patch assumes that "hg qselect" focuses on changing pushable-ness
only of already applied patches, because:
- the report message uses not "previous" (in the series) but
"applied"
- the logic to pop patches for --pop/--reapply examines
pushable-ness only of already applied ones (in fact, there are
some incorrect code paths)
2014-09-11 21:29:19 +04:00
|
|
|
guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
|
2006-08-11 01:58:10 +04:00
|
|
|
if len(unapplied) != len(old_unapplied):
|
|
|
|
ui.status(_('number of unguarded, unapplied patches has '
|
|
|
|
'changed from %d to %d\n') %
|
|
|
|
(len(old_unapplied), len(unapplied)))
|
|
|
|
if len(guarded) != len(old_guarded):
|
|
|
|
ui.status(_('number of guarded, applied patches has changed '
|
|
|
|
'from %d to %d\n') %
|
|
|
|
(len(old_guarded), len(guarded)))
|
2010-09-15 00:11:46 +04:00
|
|
|
elif opts.get('series'):
|
2006-08-09 08:42:50 +04:00
|
|
|
guards = {}
|
|
|
|
noguards = 0
|
2011-06-13 13:40:27 +04:00
|
|
|
for gs in q.seriesguards:
|
2006-08-09 08:42:50 +04:00
|
|
|
if not gs:
|
|
|
|
noguards += 1
|
|
|
|
for g in gs:
|
|
|
|
guards.setdefault(g, 0)
|
|
|
|
guards[g] += 1
|
|
|
|
if ui.verbose:
|
|
|
|
guards['NONE'] = noguards
|
|
|
|
guards = guards.items()
|
2009-07-05 13:02:00 +04:00
|
|
|
guards.sort(key=lambda x: x[0][1:])
|
2006-08-09 08:42:50 +04:00
|
|
|
if guards:
|
|
|
|
ui.note(_('guards in series file:\n'))
|
|
|
|
for guard, count in guards:
|
|
|
|
ui.note('%2d ' % count)
|
|
|
|
ui.write(guard, '\n')
|
|
|
|
else:
|
|
|
|
ui.note(_('no guards in series file\n'))
|
|
|
|
else:
|
|
|
|
if guards:
|
|
|
|
ui.note(_('active guards:\n'))
|
|
|
|
for g in guards:
|
|
|
|
ui.write(g, '\n')
|
|
|
|
else:
|
|
|
|
ui.write(_('no active guards\n'))
|
2014-09-11 21:29:19 +04:00
|
|
|
reapply = opts.get('reapply') and q.applied and q.applied[-1].name
|
2006-08-11 01:58:10 +04:00
|
|
|
popped = False
|
2010-09-15 00:11:46 +04:00
|
|
|
if opts.get('pop') or opts.get('reapply'):
|
2006-08-11 01:58:10 +04:00
|
|
|
for i in xrange(len(q.applied)):
|
2014-09-11 21:29:19 +04:00
|
|
|
if not pushable(i):
|
2006-08-11 01:58:10 +04:00
|
|
|
ui.status(_('popping guarded patches\n'))
|
|
|
|
popped = True
|
|
|
|
if i == 0:
|
|
|
|
q.pop(repo, all=True)
|
|
|
|
else:
|
2014-09-11 21:29:19 +04:00
|
|
|
q.pop(repo, q.applied[i - 1].name)
|
2006-08-11 01:58:10 +04:00
|
|
|
break
|
|
|
|
if popped:
|
|
|
|
try:
|
|
|
|
if reapply:
|
|
|
|
ui.status(_('reapplying unguarded patches\n'))
|
|
|
|
q.push(repo, reapply)
|
|
|
|
finally:
|
2011-06-13 14:02:14 +04:00
|
|
|
q.savedirty()
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qfinish",
|
|
|
|
[('a', 'applied', None, _('finish all applied changesets'))],
|
|
|
|
_('hg qfinish [-a] [REV]...'))
|
2008-06-03 11:34:14 +04:00
|
|
|
def finish(ui, repo, *revrange, **opts):
|
|
|
|
"""move applied patches into repository history
|
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Finishes the specified revisions (corresponding to applied
|
|
|
|
patches) by moving them out of mq control into regular repository
|
|
|
|
history.
|
2008-06-03 11:34:14 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
Accepts a revision range or the -a/--applied option. If --applied
|
|
|
|
is specified, all applied mq revisions are removed from mq
|
|
|
|
control. Otherwise, the given revisions must be at the base of the
|
|
|
|
stack of applied patches.
|
2008-06-03 11:34:14 +04:00
|
|
|
|
2009-07-26 03:53:49 +04:00
|
|
|
This can be especially useful if your changes have been applied to
|
|
|
|
an upstream repository, or if you are about to push your changes
|
|
|
|
to upstream.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success.
|
2008-06-03 11:34:14 +04:00
|
|
|
"""
|
2010-09-15 00:11:46 +04:00
|
|
|
if not opts.get('applied') and not revrange:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no revisions specified'))
|
2010-09-15 00:11:46 +04:00
|
|
|
elif opts.get('applied'):
|
2010-08-02 01:02:56 +04:00
|
|
|
revrange = ('qbase::qtip',) + revrange
|
2008-06-03 11:34:14 +04:00
|
|
|
|
|
|
|
q = repo.mq
|
|
|
|
if not q.applied:
|
|
|
|
ui.status(_('no patches applied\n'))
|
|
|
|
return 0
|
|
|
|
|
2011-05-13 23:06:28 +04:00
|
|
|
revs = scmutil.revrange(repo, revrange)
|
2011-11-11 01:40:34 +04:00
|
|
|
if repo['.'].rev() in revs and repo[None].files():
|
|
|
|
ui.warn(_('warning: uncommitted changes in the working directory\n'))
|
2012-08-16 00:38:42 +04:00
|
|
|
# queue.finish may changes phases but leave the responsibility to lock the
|
2012-01-18 20:06:59 +04:00
|
|
|
# repo to the caller to avoid deadlock with wlock. This command code is
|
2012-08-16 00:38:42 +04:00
|
|
|
# responsibility for this locking.
|
2016-01-16 00:14:50 +03:00
|
|
|
with repo.lock():
|
2012-01-18 20:06:59 +04:00
|
|
|
q.finish(repo, revs)
|
|
|
|
q.savedirty()
|
2008-06-03 11:34:14 +04:00
|
|
|
return 0
|
|
|
|
|
2011-05-12 16:25:08 +04:00
|
|
|
@command("qqueue",
|
|
|
|
[('l', 'list', False, _('list all available queues')),
|
2011-07-29 22:26:52 +04:00
|
|
|
('', 'active', False, _('print name of active queue')),
|
2011-05-12 16:25:08 +04:00
|
|
|
('c', 'create', False, _('create new queue')),
|
|
|
|
('', 'rename', False, _('rename active queue')),
|
|
|
|
('', 'delete', False, _('delete reference to queue')),
|
|
|
|
('', 'purge', False, _('delete queue, and remove patch dir')),
|
|
|
|
],
|
|
|
|
_('[OPTION] [QUEUE]'))
|
2010-05-29 22:32:39 +04:00
|
|
|
def qqueue(ui, repo, name=None, **opts):
|
|
|
|
'''manage multiple patch queues
|
|
|
|
|
|
|
|
Supports switching between different patch queues, as well as creating
|
|
|
|
new patch queues and deleting existing ones.
|
|
|
|
|
|
|
|
Omitting a queue name or specifying -l/--list will show you the registered
|
|
|
|
queues - by default the "normal" patches queue is registered. The currently
|
2011-07-29 22:26:52 +04:00
|
|
|
active queue will be marked with "(active)". Specifying --active will print
|
|
|
|
only the name of the active queue.
|
2010-05-29 22:32:39 +04:00
|
|
|
|
|
|
|
To create a new queue, use -c/--create. The queue is automatically made
|
|
|
|
active, except in the case where there are applied patches from the
|
|
|
|
currently active queue in the repository. Then the queue will only be
|
|
|
|
created and switching will fail.
|
|
|
|
|
|
|
|
To delete an existing queue, use --delete. You cannot delete the currently
|
|
|
|
active queue.
|
2010-09-23 18:56:32 +04:00
|
|
|
|
|
|
|
Returns 0 on success.
|
2010-05-29 22:32:39 +04:00
|
|
|
'''
|
|
|
|
q = repo.mq
|
|
|
|
_defaultqueue = 'patches'
|
2010-06-02 21:39:45 +04:00
|
|
|
_allqueues = 'patches.queues'
|
|
|
|
_activequeue = 'patches.queue'
|
2010-05-29 22:32:39 +04:00
|
|
|
|
|
|
|
def _getcurrent():
|
2010-06-02 21:39:45 +04:00
|
|
|
cur = os.path.basename(q.path)
|
|
|
|
if cur.startswith('patches-'):
|
|
|
|
cur = cur[8:]
|
|
|
|
return cur
|
2010-05-29 22:32:39 +04:00
|
|
|
|
|
|
|
def _noqueues():
|
|
|
|
try:
|
2015-01-16 01:17:12 +03:00
|
|
|
fh = repo.vfs(_allqueues, 'r')
|
2010-05-29 22:32:39 +04:00
|
|
|
fh.close()
|
|
|
|
except IOError:
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
def _getqueues():
|
|
|
|
current = _getcurrent()
|
|
|
|
|
|
|
|
try:
|
2015-01-16 01:17:12 +03:00
|
|
|
fh = repo.vfs(_allqueues, 'r')
|
2010-05-29 22:32:39 +04:00
|
|
|
queues = [queue.strip() for queue in fh if queue.strip()]
|
2010-12-24 17:23:01 +03:00
|
|
|
fh.close()
|
2010-05-29 22:32:39 +04:00
|
|
|
if current not in queues:
|
|
|
|
queues.append(current)
|
|
|
|
except IOError:
|
|
|
|
queues = [_defaultqueue]
|
|
|
|
|
|
|
|
return sorted(queues)
|
|
|
|
|
|
|
|
def _setactive(name):
|
|
|
|
if q.applied:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('new queue created, but cannot make active '
|
2012-10-05 02:00:32 +04:00
|
|
|
'as patches are applied'))
|
2010-08-15 18:48:08 +04:00
|
|
|
_setactivenocheck(name)
|
2010-05-29 22:32:39 +04:00
|
|
|
|
2010-08-15 18:48:08 +04:00
|
|
|
def _setactivenocheck(name):
|
2015-01-16 01:17:12 +03:00
|
|
|
fh = repo.vfs(_activequeue, 'w')
|
2010-06-02 21:39:45 +04:00
|
|
|
if name != 'patches':
|
|
|
|
fh.write(name)
|
2010-05-29 22:32:39 +04:00
|
|
|
fh.close()
|
|
|
|
|
|
|
|
def _addqueue(name):
|
2015-01-16 01:17:12 +03:00
|
|
|
fh = repo.vfs(_allqueues, 'a')
|
2010-05-29 22:32:39 +04:00
|
|
|
fh.write('%s\n' % (name,))
|
|
|
|
fh.close()
|
|
|
|
|
2010-08-15 15:29:46 +04:00
|
|
|
def _queuedir(name):
|
|
|
|
if name == 'patches':
|
2017-03-09 03:52:16 +03:00
|
|
|
return repo.vfs.join('patches')
|
2010-08-15 15:29:46 +04:00
|
|
|
else:
|
2017-03-09 03:52:16 +03:00
|
|
|
return repo.vfs.join('patches-' + name)
|
2010-08-15 15:29:46 +04:00
|
|
|
|
2010-06-02 21:39:45 +04:00
|
|
|
def _validname(name):
|
|
|
|
for n in name:
|
|
|
|
if n in ':\\/.':
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2010-08-19 01:34:28 +04:00
|
|
|
def _delete(name):
|
|
|
|
if name not in existing:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot delete queue that does not exist'))
|
2010-08-19 01:34:28 +04:00
|
|
|
|
|
|
|
current = _getcurrent()
|
|
|
|
|
|
|
|
if name == current:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('cannot delete currently active queue'))
|
2010-08-19 01:34:28 +04:00
|
|
|
|
2015-01-16 01:17:12 +03:00
|
|
|
fh = repo.vfs('patches.queues.new', 'w')
|
2010-08-19 01:34:28 +04:00
|
|
|
for queue in existing:
|
|
|
|
if queue == name:
|
|
|
|
continue
|
|
|
|
fh.write('%s\n' % (queue,))
|
|
|
|
fh.close()
|
2017-03-11 22:04:14 +03:00
|
|
|
repo.vfs.rename('patches.queues.new', _allqueues)
|
2010-08-19 01:34:28 +04:00
|
|
|
|
2011-07-29 22:26:52 +04:00
|
|
|
if not name or opts.get('list') or opts.get('active'):
|
2010-05-29 22:32:39 +04:00
|
|
|
current = _getcurrent()
|
2011-07-29 22:26:52 +04:00
|
|
|
if opts.get('active'):
|
|
|
|
ui.write('%s\n' % (current,))
|
|
|
|
return
|
2010-05-29 22:32:39 +04:00
|
|
|
for queue in _getqueues():
|
|
|
|
ui.write('%s' % (queue,))
|
2010-08-09 00:49:01 +04:00
|
|
|
if queue == current and not ui.quiet:
|
2010-05-29 22:32:39 +04:00
|
|
|
ui.write(_(' (active)\n'))
|
|
|
|
else:
|
|
|
|
ui.write('\n')
|
|
|
|
return
|
|
|
|
|
2010-06-02 21:39:45 +04:00
|
|
|
if not _validname(name):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(
|
2010-06-02 21:39:45 +04:00
|
|
|
_('invalid queue name, may not contain the characters ":\\/."'))
|
|
|
|
|
2016-08-07 18:00:45 +03:00
|
|
|
with repo.wlock():
|
|
|
|
existing = _getqueues()
|
|
|
|
|
|
|
|
if opts.get('create'):
|
|
|
|
if name in existing:
|
|
|
|
raise error.Abort(_('queue "%s" already exists') % name)
|
|
|
|
if _noqueues():
|
|
|
|
_addqueue(_defaultqueue)
|
|
|
|
_addqueue(name)
|
|
|
|
_setactive(name)
|
|
|
|
elif opts.get('rename'):
|
|
|
|
current = _getcurrent()
|
|
|
|
if name == current:
|
|
|
|
raise error.Abort(_('can\'t rename "%s" to its current name')
|
|
|
|
% name)
|
|
|
|
if name in existing:
|
|
|
|
raise error.Abort(_('queue "%s" already exists') % name)
|
|
|
|
|
|
|
|
olddir = _queuedir(current)
|
|
|
|
newdir = _queuedir(name)
|
|
|
|
|
|
|
|
if os.path.exists(newdir):
|
|
|
|
raise error.Abort(_('non-queue directory "%s" already exists') %
|
|
|
|
newdir)
|
|
|
|
|
|
|
|
fh = repo.vfs('patches.queues.new', 'w')
|
|
|
|
for queue in existing:
|
|
|
|
if queue == current:
|
|
|
|
fh.write('%s\n' % (name,))
|
|
|
|
if os.path.exists(olddir):
|
|
|
|
util.rename(olddir, newdir)
|
|
|
|
else:
|
|
|
|
fh.write('%s\n' % (queue,))
|
|
|
|
fh.close()
|
2017-03-11 22:04:14 +03:00
|
|
|
repo.vfs.rename('patches.queues.new', _allqueues)
|
2016-08-07 18:00:45 +03:00
|
|
|
_setactivenocheck(name)
|
|
|
|
elif opts.get('delete'):
|
2010-08-19 01:37:19 +04:00
|
|
|
_delete(name)
|
2016-08-07 18:00:45 +03:00
|
|
|
elif opts.get('purge'):
|
|
|
|
if name in existing:
|
|
|
|
_delete(name)
|
|
|
|
qdir = _queuedir(name)
|
|
|
|
if os.path.exists(qdir):
|
|
|
|
shutil.rmtree(qdir)
|
|
|
|
else:
|
|
|
|
if name not in existing:
|
|
|
|
raise error.Abort(_('use --create to create a new queue'))
|
|
|
|
_setactive(name)
|
2010-05-29 22:32:39 +04:00
|
|
|
|
2012-01-18 20:18:38 +04:00
|
|
|
def mqphasedefaults(repo, roots):
|
|
|
|
"""callback used to set mq changeset as secret when no phase data exists"""
|
|
|
|
if repo.mq.applied:
|
2012-01-30 20:39:05 +04:00
|
|
|
if repo.ui.configbool('mq', 'secret', False):
|
|
|
|
mqphase = phases.secret
|
|
|
|
else:
|
|
|
|
mqphase = phases.draft
|
2012-01-22 07:39:27 +04:00
|
|
|
qbase = repo[repo.mq.applied[0].node]
|
2012-01-30 20:39:05 +04:00
|
|
|
roots[mqphase].add(qbase.node())
|
2012-01-18 20:18:38 +04:00
|
|
|
return roots
|
|
|
|
|
2006-02-28 21:25:26 +03:00
|
|
|
def reposetup(ui, repo):
|
2006-08-08 01:59:32 +04:00
|
|
|
class mqrepo(repo.__class__):
|
2013-07-14 19:23:07 +04:00
|
|
|
@localrepo.unfilteredpropertycache
|
2009-05-04 05:35:42 +04:00
|
|
|
def mq(self):
|
2013-04-19 01:53:39 +04:00
|
|
|
return queue(self.ui, self.baseui, self.path)
|
2009-05-04 05:35:42 +04:00
|
|
|
|
2014-03-03 14:41:26 +04:00
|
|
|
def invalidateall(self):
|
|
|
|
super(mqrepo, self).invalidateall()
|
|
|
|
if localrepo.hasunfilteredcache(self, 'mq'):
|
2014-03-03 14:41:30 +04:00
|
|
|
# recreate mq in case queue path was changed
|
|
|
|
delattr(self.unfiltered(), 'mq')
|
2014-03-03 14:41:26 +04:00
|
|
|
|
2011-06-13 15:53:58 +04:00
|
|
|
def abortifwdirpatched(self, errmsg, force=False):
|
2013-08-29 20:22:15 +04:00
|
|
|
if self.mq.applied and self.mq.checkapplied and not force:
|
2011-03-02 16:09:06 +03:00
|
|
|
parents = self.dirstate.parents()
|
|
|
|
patches = [s.node for s in self.mq.applied]
|
|
|
|
if parents[0] in patches or parents[1] in patches:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(errmsg)
|
2006-10-01 21:26:33 +04:00
|
|
|
|
2009-06-04 02:07:41 +04:00
|
|
|
def commit(self, text="", user=None, date=None, match=None,
|
2017-03-15 09:46:48 +03:00
|
|
|
force=False, editor=False, extra=None):
|
|
|
|
if extra is None:
|
|
|
|
extra = {}
|
2011-06-13 15:53:58 +04:00
|
|
|
self.abortifwdirpatched(
|
2006-08-11 03:10:59 +04:00
|
|
|
_('cannot commit over an applied mq patch'),
|
|
|
|
force)
|
2006-08-10 22:18:48 +04:00
|
|
|
|
2009-06-04 02:07:41 +04:00
|
|
|
return super(mqrepo, self).commit(text, user, date, match, force,
|
|
|
|
editor, extra)
|
2006-08-10 22:18:48 +04:00
|
|
|
|
2014-04-02 00:45:48 +04:00
|
|
|
def checkpush(self, pushop):
|
|
|
|
if self.mq.applied and self.mq.checkapplied and not pushop.force:
|
2012-01-20 21:45:29 +04:00
|
|
|
outapplied = [e.node for e in self.mq.applied]
|
2014-04-02 00:45:48 +04:00
|
|
|
if pushop.revs:
|
2012-01-20 21:45:29 +04:00
|
|
|
# Assume applied patches have no non-patch descendants and
|
|
|
|
# are not on remote already. Filtering any changeset not
|
|
|
|
# pushed.
|
2014-04-02 00:45:48 +04:00
|
|
|
heads = set(pushop.revs)
|
2012-01-20 21:45:29 +04:00
|
|
|
for node in reversed(outapplied):
|
|
|
|
if node in heads:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
outapplied.pop()
|
|
|
|
# looking for pushed and shared changeset
|
|
|
|
for node in outapplied:
|
2012-11-20 23:52:47 +04:00
|
|
|
if self[node].phase() < phases.secret:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('source has mq patches applied'))
|
2012-01-20 21:45:29 +04:00
|
|
|
# no non-secret patches pushed
|
2014-04-02 00:45:48 +04:00
|
|
|
super(mqrepo, self).checkpush(pushop)
|
2006-10-01 21:26:33 +04:00
|
|
|
|
2009-07-16 18:39:41 +04:00
|
|
|
def _findtags(self):
|
|
|
|
'''augment tags from base class with patch tags'''
|
|
|
|
result = super(mqrepo, self)._findtags()
|
2006-07-29 00:08:21 +04:00
|
|
|
|
2006-07-29 00:08:25 +04:00
|
|
|
q = self.mq
|
2006-07-29 00:08:21 +04:00
|
|
|
if not q.applied:
|
2009-07-16 18:39:41 +04:00
|
|
|
return result
|
2006-07-29 00:08:21 +04:00
|
|
|
|
2010-03-14 02:36:10 +03:00
|
|
|
mqtags = [(patch.node, patch.name) for patch in q.applied]
|
2008-02-04 02:03:46 +03:00
|
|
|
|
2011-03-01 02:41:40 +03:00
|
|
|
try:
|
2012-10-08 22:01:40 +04:00
|
|
|
# for now ignore filtering business
|
|
|
|
self.unfiltered().changelog.rev(mqtags[-1][0])
|
2011-06-13 00:30:39 +04:00
|
|
|
except error.LookupError:
|
2008-08-31 18:12:03 +04:00
|
|
|
self.ui.warn(_('mq status file refers to unknown node %s\n')
|
2009-01-12 19:59:08 +03:00
|
|
|
% short(mqtags[-1][0]))
|
2009-07-16 18:39:41 +04:00
|
|
|
return result
|
2008-02-04 02:03:46 +03:00
|
|
|
|
2013-02-11 19:21:48 +04:00
|
|
|
# do not add fake tags for filtered revisions
|
|
|
|
included = self.changelog.hasnode
|
|
|
|
mqtags = [mqt for mqt in mqtags if included(mqt[0])]
|
|
|
|
if not mqtags:
|
|
|
|
return result
|
|
|
|
|
2006-07-29 00:08:21 +04:00
|
|
|
mqtags.append((mqtags[-1][0], 'qtip'))
|
|
|
|
mqtags.append((mqtags[0][0], 'qbase'))
|
2007-03-14 20:45:18 +03:00
|
|
|
mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
|
2009-07-16 18:39:41 +04:00
|
|
|
tags = result[0]
|
2006-07-29 00:08:21 +04:00
|
|
|
for patch in mqtags:
|
2009-07-16 18:39:41 +04:00
|
|
|
if patch[1] in tags:
|
2012-06-12 16:18:18 +04:00
|
|
|
self.ui.warn(_('tag %s overrides mq patch of the same '
|
2012-05-12 17:54:54 +04:00
|
|
|
'name\n') % patch[1])
|
2006-07-29 00:08:21 +04:00
|
|
|
else:
|
2009-07-16 18:39:41 +04:00
|
|
|
tags[patch[1]] = patch[0]
|
2006-07-26 23:22:04 +04:00
|
|
|
|
2009-07-16 18:39:41 +04:00
|
|
|
return result
|
2006-07-26 23:22:04 +04:00
|
|
|
|
2006-08-11 04:33:08 +04:00
|
|
|
if repo.local():
|
|
|
|
repo.__class__ = mqrepo
|
2006-02-28 21:25:26 +03:00
|
|
|
|
2012-01-18 20:18:38 +04:00
|
|
|
repo._phasedefaults.append(mqphasedefaults)
|
|
|
|
|
2008-10-23 02:34:52 +04:00
|
|
|
def mqimport(orig, ui, repo, *args, **kwargs):
|
2012-04-13 17:07:13 +04:00
|
|
|
if (util.safehasattr(repo, 'abortifwdirpatched')
|
2010-01-25 09:05:27 +03:00
|
|
|
and not kwargs.get('no_commit', False)):
|
2011-06-13 15:53:58 +04:00
|
|
|
repo.abortifwdirpatched(_('cannot import over an applied patch'),
|
2008-10-23 02:34:52 +04:00
|
|
|
kwargs.get('force'))
|
|
|
|
return orig(ui, repo, *args, **kwargs)
|
|
|
|
|
2010-02-08 12:50:00 +03:00
|
|
|
def mqinit(orig, ui, *args, **kwargs):
|
2010-02-28 21:43:21 +03:00
|
|
|
mq = kwargs.pop('mq', None)
|
2010-02-08 12:50:00 +03:00
|
|
|
|
|
|
|
if not mq:
|
|
|
|
return orig(ui, *args, **kwargs)
|
|
|
|
|
2010-03-14 20:46:38 +03:00
|
|
|
if args:
|
|
|
|
repopath = args[0]
|
|
|
|
if not hg.islocal(repopath):
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('only a local queue repository '
|
2010-03-14 20:46:38 +03:00
|
|
|
'may be initialized'))
|
|
|
|
else:
|
2016-11-22 21:33:11 +03:00
|
|
|
repopath = cmdutil.findrepo(pycompat.getcwd())
|
2010-03-14 20:46:38 +03:00
|
|
|
if not repopath:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('there is no Mercurial repository here '
|
2010-03-14 20:46:38 +03:00
|
|
|
'(.hg not found)'))
|
2010-02-08 12:50:00 +03:00
|
|
|
repo = hg.repository(ui, repopath)
|
2010-02-15 11:16:59 +03:00
|
|
|
return qinit(ui, repo, True)
|
2010-02-08 12:50:00 +03:00
|
|
|
|
2008-12-23 20:46:40 +03:00
|
|
|
def mqcommand(orig, ui, repo, *args, **kwargs):
|
|
|
|
"""Add --mq option to operate on patch repository instead of main"""
|
|
|
|
|
|
|
|
# some commands do not like getting unknown options
|
2017-05-05 02:18:42 +03:00
|
|
|
mq = kwargs.pop(r'mq', None)
|
2008-12-23 20:46:40 +03:00
|
|
|
|
|
|
|
if not mq:
|
|
|
|
return orig(ui, repo, *args, **kwargs)
|
|
|
|
|
|
|
|
q = repo.mq
|
|
|
|
r = q.qrepo()
|
|
|
|
if not r:
|
2015-10-08 22:55:45 +03:00
|
|
|
raise error.Abort(_('no queue repository'))
|
2010-02-10 21:58:11 +03:00
|
|
|
return orig(r.ui, r, *args, **kwargs)
|
2008-12-23 20:46:40 +03:00
|
|
|
|
2013-05-14 22:23:16 +04:00
|
|
|
def summaryhook(ui, repo):
|
2010-05-06 05:53:45 +04:00
|
|
|
q = repo.mq
|
|
|
|
m = []
|
|
|
|
a, u = len(q.applied), len(q.unapplied(repo))
|
|
|
|
if a:
|
2010-05-08 00:51:45 +04:00
|
|
|
m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
|
2010-05-06 05:53:45 +04:00
|
|
|
if u:
|
2010-05-08 00:51:45 +04:00
|
|
|
m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
|
2010-05-06 05:53:45 +04:00
|
|
|
if m:
|
2012-10-30 22:59:28 +04:00
|
|
|
# i18n: column positioning for "hg summary"
|
|
|
|
ui.write(_("mq: %s\n") % ', '.join(m))
|
2010-05-06 05:53:45 +04:00
|
|
|
else:
|
2012-10-30 22:59:28 +04:00
|
|
|
# i18n: column positioning for "hg summary"
|
2010-05-08 01:28:07 +04:00
|
|
|
ui.note(_("mq: (empty queue)\n"))
|
2010-05-06 05:53:45 +04:00
|
|
|
|
2016-03-08 17:04:53 +03:00
|
|
|
revsetpredicate = registrar.revsetpredicate()
|
2015-12-29 17:58:30 +03:00
|
|
|
|
|
|
|
@revsetpredicate('mq()')
|
2011-05-06 17:07:35 +04:00
|
|
|
def revsetmq(repo, subset, x):
|
2015-12-29 17:58:30 +03:00
|
|
|
"""Changesets managed by MQ.
|
2011-05-06 17:07:35 +04:00
|
|
|
"""
|
2017-02-19 12:19:33 +03:00
|
|
|
revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
|
2011-05-06 17:07:35 +04:00
|
|
|
applied = set([repo[r.node].rev() for r in repo.mq.applied])
|
2017-02-19 12:16:09 +03:00
|
|
|
return smartset.baseset([r for r in subset if r in applied])
|
2011-05-06 17:07:35 +04:00
|
|
|
|
|
|
|
# tell hggettext to extract docstrings from these functions:
|
|
|
|
i18nfunctions = [revsetmq]
|
|
|
|
|
2012-06-09 07:27:59 +04:00
|
|
|
def extsetup(ui):
|
|
|
|
# Ensure mq wrappers are called first, regardless of extension load order by
|
|
|
|
# NOT wrapping in uisetup() and instead deferring to init stage two here.
|
2010-03-06 02:24:52 +03:00
|
|
|
mqopt = [('', 'mq', None, _("operate on patch repository"))]
|
2010-02-08 12:50:00 +03:00
|
|
|
|
2008-10-23 02:34:52 +04:00
|
|
|
extensions.wrapcommand(commands.table, 'import', mqimport)
|
2013-05-14 22:23:16 +04:00
|
|
|
cmdutil.summaryhooks.add('mq', summaryhook)
|
2010-02-08 12:50:00 +03:00
|
|
|
|
|
|
|
entry = extensions.wrapcommand(commands.table, 'init', mqinit)
|
|
|
|
entry[1].extend(mqopt)
|
|
|
|
|
2010-08-13 18:05:08 +04:00
|
|
|
def dotable(cmdtable):
|
2016-01-01 16:16:25 +03:00
|
|
|
for cmd, entry in cmdtable.iteritems():
|
2010-08-13 18:05:08 +04:00
|
|
|
cmd = cmdutil.parsealiases(cmd)[0]
|
2016-01-01 16:16:25 +03:00
|
|
|
func = entry[0]
|
2016-11-22 05:51:23 +03:00
|
|
|
if func.norepo:
|
2010-08-13 18:05:08 +04:00
|
|
|
continue
|
|
|
|
entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
|
|
|
|
entry[1].extend(mqopt)
|
|
|
|
|
|
|
|
dotable(commands.table)
|
|
|
|
|
|
|
|
for extname, extmodule in extensions.extensions():
|
|
|
|
if extmodule.__file__ != __file__:
|
|
|
|
dotable(getattr(extmodule, 'cmdtable', {}))
|
2008-10-19 00:22:29 +04:00
|
|
|
|
2010-04-03 00:22:17 +04:00
|
|
|
colortable = {'qguard.negative': 'red',
|
|
|
|
'qguard.positive': 'yellow',
|
|
|
|
'qguard.unguarded': 'green',
|
|
|
|
'qseries.applied': 'blue bold underline',
|
|
|
|
'qseries.guarded': 'black bold',
|
|
|
|
'qseries.missing': 'red bold',
|
|
|
|
'qseries.unapplied': 'black bold'}
|