##// END OF EJS Templates
mq: reject new patch name containing leading/trailing whitespace...
Yuya Nishihara -
r31556:448acdee default
parent child Browse files
Show More
@@ -1,3610 +1,3614 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help COMMAND` for more details)::
17 Common tasks (use :hg:`help COMMAND` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behavior can be configured with::
31 files creations or deletions. This behavior can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from __future__ import absolute_import
65 from __future__ import absolute_import
66
66
67 import errno
67 import errno
68 import os
68 import os
69 import re
69 import re
70 import shutil
70 import shutil
71 from mercurial.i18n import _
71 from mercurial.i18n import _
72 from mercurial.node import (
72 from mercurial.node import (
73 bin,
73 bin,
74 hex,
74 hex,
75 nullid,
75 nullid,
76 nullrev,
76 nullrev,
77 short,
77 short,
78 )
78 )
79 from mercurial import (
79 from mercurial import (
80 cmdutil,
80 cmdutil,
81 commands,
81 commands,
82 dirstateguard,
82 dirstateguard,
83 error,
83 error,
84 extensions,
84 extensions,
85 hg,
85 hg,
86 localrepo,
86 localrepo,
87 lock as lockmod,
87 lock as lockmod,
88 patch as patchmod,
88 patch as patchmod,
89 phases,
89 phases,
90 pycompat,
90 pycompat,
91 registrar,
91 registrar,
92 revsetlang,
92 revsetlang,
93 scmutil,
93 scmutil,
94 smartset,
94 smartset,
95 subrepo,
95 subrepo,
96 util,
96 util,
97 vfs as vfsmod,
97 vfs as vfsmod,
98 )
98 )
99
99
100 release = lockmod.release
100 release = lockmod.release
101 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
101 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
102
102
103 cmdtable = {}
103 cmdtable = {}
104 command = cmdutil.command(cmdtable)
104 command = cmdutil.command(cmdtable)
105 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
105 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
106 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
106 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
107 # be specifying the version(s) of Mercurial they are tested with, or
107 # be specifying the version(s) of Mercurial they are tested with, or
108 # leave the attribute unspecified.
108 # leave the attribute unspecified.
109 testedwith = 'ships-with-hg-core'
109 testedwith = 'ships-with-hg-core'
110
110
111 # force load strip extension formerly included in mq and import some utility
111 # force load strip extension formerly included in mq and import some utility
112 try:
112 try:
113 stripext = extensions.find('strip')
113 stripext = extensions.find('strip')
114 except KeyError:
114 except KeyError:
115 # note: load is lazy so we could avoid the try-except,
115 # note: load is lazy so we could avoid the try-except,
116 # but I (marmoute) prefer this explicit code.
116 # but I (marmoute) prefer this explicit code.
117 class dummyui(object):
117 class dummyui(object):
118 def debug(self, msg):
118 def debug(self, msg):
119 pass
119 pass
120 stripext = extensions.load(dummyui(), 'strip', '')
120 stripext = extensions.load(dummyui(), 'strip', '')
121
121
122 strip = stripext.strip
122 strip = stripext.strip
123 checksubstate = stripext.checksubstate
123 checksubstate = stripext.checksubstate
124 checklocalchanges = stripext.checklocalchanges
124 checklocalchanges = stripext.checklocalchanges
125
125
126
126
127 # Patch names looks like unix-file names.
127 # Patch names looks like unix-file names.
128 # They must be joinable with queue directory and result in the patch path.
128 # They must be joinable with queue directory and result in the patch path.
129 normname = util.normpath
129 normname = util.normpath
130
130
131 class statusentry(object):
131 class statusentry(object):
132 def __init__(self, node, name):
132 def __init__(self, node, name):
133 self.node, self.name = node, name
133 self.node, self.name = node, name
134 def __repr__(self):
134 def __repr__(self):
135 return hex(self.node) + ':' + self.name
135 return hex(self.node) + ':' + self.name
136
136
137 # The order of the headers in 'hg export' HG patches:
137 # The order of the headers in 'hg export' HG patches:
138 HGHEADERS = [
138 HGHEADERS = [
139 # '# HG changeset patch',
139 # '# HG changeset patch',
140 '# User ',
140 '# User ',
141 '# Date ',
141 '# Date ',
142 '# ',
142 '# ',
143 '# Branch ',
143 '# Branch ',
144 '# Node ID ',
144 '# Node ID ',
145 '# Parent ', # can occur twice for merges - but that is not relevant for mq
145 '# Parent ', # can occur twice for merges - but that is not relevant for mq
146 ]
146 ]
147 # The order of headers in plain 'mail style' patches:
147 # The order of headers in plain 'mail style' patches:
148 PLAINHEADERS = {
148 PLAINHEADERS = {
149 'from': 0,
149 'from': 0,
150 'date': 1,
150 'date': 1,
151 'subject': 2,
151 'subject': 2,
152 }
152 }
153
153
154 def inserthgheader(lines, header, value):
154 def inserthgheader(lines, header, value):
155 """Assuming lines contains a HG patch header, add a header line with value.
155 """Assuming lines contains a HG patch header, add a header line with value.
156 >>> try: inserthgheader([], '# Date ', 'z')
156 >>> try: inserthgheader([], '# Date ', 'z')
157 ... except ValueError, inst: print "oops"
157 ... except ValueError, inst: print "oops"
158 oops
158 oops
159 >>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
159 >>> inserthgheader(['# HG changeset patch'], '# Date ', 'z')
160 ['# HG changeset patch', '# Date z']
160 ['# HG changeset patch', '# Date z']
161 >>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
161 >>> inserthgheader(['# HG changeset patch', ''], '# Date ', 'z')
162 ['# HG changeset patch', '# Date z', '']
162 ['# HG changeset patch', '# Date z', '']
163 >>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
163 >>> inserthgheader(['# HG changeset patch', '# User y'], '# Date ', 'z')
164 ['# HG changeset patch', '# User y', '# Date z']
164 ['# HG changeset patch', '# User y', '# Date z']
165 >>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
165 >>> inserthgheader(['# HG changeset patch', '# Date x', '# User y'],
166 ... '# User ', 'z')
166 ... '# User ', 'z')
167 ['# HG changeset patch', '# Date x', '# User z']
167 ['# HG changeset patch', '# Date x', '# User z']
168 >>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
168 >>> inserthgheader(['# HG changeset patch', '# Date y'], '# Date ', 'z')
169 ['# HG changeset patch', '# Date z']
169 ['# HG changeset patch', '# Date z']
170 >>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
170 >>> inserthgheader(['# HG changeset patch', '', '# Date y'], '# Date ', 'z')
171 ['# HG changeset patch', '# Date z', '', '# Date y']
171 ['# HG changeset patch', '# Date z', '', '# Date y']
172 >>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
172 >>> inserthgheader(['# HG changeset patch', '# Parent y'], '# Date ', 'z')
173 ['# HG changeset patch', '# Date z', '# Parent y']
173 ['# HG changeset patch', '# Date z', '# Parent y']
174 """
174 """
175 start = lines.index('# HG changeset patch') + 1
175 start = lines.index('# HG changeset patch') + 1
176 newindex = HGHEADERS.index(header)
176 newindex = HGHEADERS.index(header)
177 bestpos = len(lines)
177 bestpos = len(lines)
178 for i in range(start, len(lines)):
178 for i in range(start, len(lines)):
179 line = lines[i]
179 line = lines[i]
180 if not line.startswith('# '):
180 if not line.startswith('# '):
181 bestpos = min(bestpos, i)
181 bestpos = min(bestpos, i)
182 break
182 break
183 for lineindex, h in enumerate(HGHEADERS):
183 for lineindex, h in enumerate(HGHEADERS):
184 if line.startswith(h):
184 if line.startswith(h):
185 if lineindex == newindex:
185 if lineindex == newindex:
186 lines[i] = header + value
186 lines[i] = header + value
187 return lines
187 return lines
188 if lineindex > newindex:
188 if lineindex > newindex:
189 bestpos = min(bestpos, i)
189 bestpos = min(bestpos, i)
190 break # next line
190 break # next line
191 lines.insert(bestpos, header + value)
191 lines.insert(bestpos, header + value)
192 return lines
192 return lines
193
193
194 def insertplainheader(lines, header, value):
194 def insertplainheader(lines, header, value):
195 """For lines containing a plain patch header, add a header line with value.
195 """For lines containing a plain patch header, add a header line with value.
196 >>> insertplainheader([], 'Date', 'z')
196 >>> insertplainheader([], 'Date', 'z')
197 ['Date: z']
197 ['Date: z']
198 >>> insertplainheader([''], 'Date', 'z')
198 >>> insertplainheader([''], 'Date', 'z')
199 ['Date: z', '']
199 ['Date: z', '']
200 >>> insertplainheader(['x'], 'Date', 'z')
200 >>> insertplainheader(['x'], 'Date', 'z')
201 ['Date: z', '', 'x']
201 ['Date: z', '', 'x']
202 >>> insertplainheader(['From: y', 'x'], 'Date', 'z')
202 >>> insertplainheader(['From: y', 'x'], 'Date', 'z')
203 ['From: y', 'Date: z', '', 'x']
203 ['From: y', 'Date: z', '', 'x']
204 >>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
204 >>> insertplainheader([' date : x', ' from : y', ''], 'From', 'z')
205 [' date : x', 'From: z', '']
205 [' date : x', 'From: z', '']
206 >>> insertplainheader(['', 'Date: y'], 'Date', 'z')
206 >>> insertplainheader(['', 'Date: y'], 'Date', 'z')
207 ['Date: z', '', 'Date: y']
207 ['Date: z', '', 'Date: y']
208 >>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
208 >>> insertplainheader(['foo: bar', 'DATE: z', 'x'], 'From', 'y')
209 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
209 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
210 """
210 """
211 newprio = PLAINHEADERS[header.lower()]
211 newprio = PLAINHEADERS[header.lower()]
212 bestpos = len(lines)
212 bestpos = len(lines)
213 for i, line in enumerate(lines):
213 for i, line in enumerate(lines):
214 if ':' in line:
214 if ':' in line:
215 lheader = line.split(':', 1)[0].strip().lower()
215 lheader = line.split(':', 1)[0].strip().lower()
216 lprio = PLAINHEADERS.get(lheader, newprio + 1)
216 lprio = PLAINHEADERS.get(lheader, newprio + 1)
217 if lprio == newprio:
217 if lprio == newprio:
218 lines[i] = '%s: %s' % (header, value)
218 lines[i] = '%s: %s' % (header, value)
219 return lines
219 return lines
220 if lprio > newprio and i < bestpos:
220 if lprio > newprio and i < bestpos:
221 bestpos = i
221 bestpos = i
222 else:
222 else:
223 if line:
223 if line:
224 lines.insert(i, '')
224 lines.insert(i, '')
225 if i < bestpos:
225 if i < bestpos:
226 bestpos = i
226 bestpos = i
227 break
227 break
228 lines.insert(bestpos, '%s: %s' % (header, value))
228 lines.insert(bestpos, '%s: %s' % (header, value))
229 return lines
229 return lines
230
230
231 class patchheader(object):
231 class patchheader(object):
232 def __init__(self, pf, plainmode=False):
232 def __init__(self, pf, plainmode=False):
233 def eatdiff(lines):
233 def eatdiff(lines):
234 while lines:
234 while lines:
235 l = lines[-1]
235 l = lines[-1]
236 if (l.startswith("diff -") or
236 if (l.startswith("diff -") or
237 l.startswith("Index:") or
237 l.startswith("Index:") or
238 l.startswith("===========")):
238 l.startswith("===========")):
239 del lines[-1]
239 del lines[-1]
240 else:
240 else:
241 break
241 break
242 def eatempty(lines):
242 def eatempty(lines):
243 while lines:
243 while lines:
244 if not lines[-1].strip():
244 if not lines[-1].strip():
245 del lines[-1]
245 del lines[-1]
246 else:
246 else:
247 break
247 break
248
248
249 message = []
249 message = []
250 comments = []
250 comments = []
251 user = None
251 user = None
252 date = None
252 date = None
253 parent = None
253 parent = None
254 format = None
254 format = None
255 subject = None
255 subject = None
256 branch = None
256 branch = None
257 nodeid = None
257 nodeid = None
258 diffstart = 0
258 diffstart = 0
259
259
260 for line in file(pf):
260 for line in file(pf):
261 line = line.rstrip()
261 line = line.rstrip()
262 if (line.startswith('diff --git')
262 if (line.startswith('diff --git')
263 or (diffstart and line.startswith('+++ '))):
263 or (diffstart and line.startswith('+++ '))):
264 diffstart = 2
264 diffstart = 2
265 break
265 break
266 diffstart = 0 # reset
266 diffstart = 0 # reset
267 if line.startswith("--- "):
267 if line.startswith("--- "):
268 diffstart = 1
268 diffstart = 1
269 continue
269 continue
270 elif format == "hgpatch":
270 elif format == "hgpatch":
271 # parse values when importing the result of an hg export
271 # parse values when importing the result of an hg export
272 if line.startswith("# User "):
272 if line.startswith("# User "):
273 user = line[7:]
273 user = line[7:]
274 elif line.startswith("# Date "):
274 elif line.startswith("# Date "):
275 date = line[7:]
275 date = line[7:]
276 elif line.startswith("# Parent "):
276 elif line.startswith("# Parent "):
277 parent = line[9:].lstrip() # handle double trailing space
277 parent = line[9:].lstrip() # handle double trailing space
278 elif line.startswith("# Branch "):
278 elif line.startswith("# Branch "):
279 branch = line[9:]
279 branch = line[9:]
280 elif line.startswith("# Node ID "):
280 elif line.startswith("# Node ID "):
281 nodeid = line[10:]
281 nodeid = line[10:]
282 elif not line.startswith("# ") and line:
282 elif not line.startswith("# ") and line:
283 message.append(line)
283 message.append(line)
284 format = None
284 format = None
285 elif line == '# HG changeset patch':
285 elif line == '# HG changeset patch':
286 message = []
286 message = []
287 format = "hgpatch"
287 format = "hgpatch"
288 elif (format != "tagdone" and (line.startswith("Subject: ") or
288 elif (format != "tagdone" and (line.startswith("Subject: ") or
289 line.startswith("subject: "))):
289 line.startswith("subject: "))):
290 subject = line[9:]
290 subject = line[9:]
291 format = "tag"
291 format = "tag"
292 elif (format != "tagdone" and (line.startswith("From: ") or
292 elif (format != "tagdone" and (line.startswith("From: ") or
293 line.startswith("from: "))):
293 line.startswith("from: "))):
294 user = line[6:]
294 user = line[6:]
295 format = "tag"
295 format = "tag"
296 elif (format != "tagdone" and (line.startswith("Date: ") or
296 elif (format != "tagdone" and (line.startswith("Date: ") or
297 line.startswith("date: "))):
297 line.startswith("date: "))):
298 date = line[6:]
298 date = line[6:]
299 format = "tag"
299 format = "tag"
300 elif format == "tag" and line == "":
300 elif format == "tag" and line == "":
301 # when looking for tags (subject: from: etc) they
301 # when looking for tags (subject: from: etc) they
302 # end once you find a blank line in the source
302 # end once you find a blank line in the source
303 format = "tagdone"
303 format = "tagdone"
304 elif message or line:
304 elif message or line:
305 message.append(line)
305 message.append(line)
306 comments.append(line)
306 comments.append(line)
307
307
308 eatdiff(message)
308 eatdiff(message)
309 eatdiff(comments)
309 eatdiff(comments)
310 # Remember the exact starting line of the patch diffs before consuming
310 # Remember the exact starting line of the patch diffs before consuming
311 # empty lines, for external use by TortoiseHg and others
311 # empty lines, for external use by TortoiseHg and others
312 self.diffstartline = len(comments)
312 self.diffstartline = len(comments)
313 eatempty(message)
313 eatempty(message)
314 eatempty(comments)
314 eatempty(comments)
315
315
316 # make sure message isn't empty
316 # make sure message isn't empty
317 if format and format.startswith("tag") and subject:
317 if format and format.startswith("tag") and subject:
318 message.insert(0, subject)
318 message.insert(0, subject)
319
319
320 self.message = message
320 self.message = message
321 self.comments = comments
321 self.comments = comments
322 self.user = user
322 self.user = user
323 self.date = date
323 self.date = date
324 self.parent = parent
324 self.parent = parent
325 # nodeid and branch are for external use by TortoiseHg and others
325 # nodeid and branch are for external use by TortoiseHg and others
326 self.nodeid = nodeid
326 self.nodeid = nodeid
327 self.branch = branch
327 self.branch = branch
328 self.haspatch = diffstart > 1
328 self.haspatch = diffstart > 1
329 self.plainmode = (plainmode or
329 self.plainmode = (plainmode or
330 '# HG changeset patch' not in self.comments and
330 '# HG changeset patch' not in self.comments and
331 any(c.startswith('Date: ') or
331 any(c.startswith('Date: ') or
332 c.startswith('From: ')
332 c.startswith('From: ')
333 for c in self.comments))
333 for c in self.comments))
334
334
335 def setuser(self, user):
335 def setuser(self, user):
336 try:
336 try:
337 inserthgheader(self.comments, '# User ', user)
337 inserthgheader(self.comments, '# User ', user)
338 except ValueError:
338 except ValueError:
339 if self.plainmode:
339 if self.plainmode:
340 insertplainheader(self.comments, 'From', user)
340 insertplainheader(self.comments, 'From', user)
341 else:
341 else:
342 tmp = ['# HG changeset patch', '# User ' + user]
342 tmp = ['# HG changeset patch', '# User ' + user]
343 self.comments = tmp + self.comments
343 self.comments = tmp + self.comments
344 self.user = user
344 self.user = user
345
345
346 def setdate(self, date):
346 def setdate(self, date):
347 try:
347 try:
348 inserthgheader(self.comments, '# Date ', date)
348 inserthgheader(self.comments, '# Date ', date)
349 except ValueError:
349 except ValueError:
350 if self.plainmode:
350 if self.plainmode:
351 insertplainheader(self.comments, 'Date', date)
351 insertplainheader(self.comments, 'Date', date)
352 else:
352 else:
353 tmp = ['# HG changeset patch', '# Date ' + date]
353 tmp = ['# HG changeset patch', '# Date ' + date]
354 self.comments = tmp + self.comments
354 self.comments = tmp + self.comments
355 self.date = date
355 self.date = date
356
356
357 def setparent(self, parent):
357 def setparent(self, parent):
358 try:
358 try:
359 inserthgheader(self.comments, '# Parent ', parent)
359 inserthgheader(self.comments, '# Parent ', parent)
360 except ValueError:
360 except ValueError:
361 if not self.plainmode:
361 if not self.plainmode:
362 tmp = ['# HG changeset patch', '# Parent ' + parent]
362 tmp = ['# HG changeset patch', '# Parent ' + parent]
363 self.comments = tmp + self.comments
363 self.comments = tmp + self.comments
364 self.parent = parent
364 self.parent = parent
365
365
366 def setmessage(self, message):
366 def setmessage(self, message):
367 if self.comments:
367 if self.comments:
368 self._delmsg()
368 self._delmsg()
369 self.message = [message]
369 self.message = [message]
370 if message:
370 if message:
371 if self.plainmode and self.comments and self.comments[-1]:
371 if self.plainmode and self.comments and self.comments[-1]:
372 self.comments.append('')
372 self.comments.append('')
373 self.comments.append(message)
373 self.comments.append(message)
374
374
375 def __str__(self):
375 def __str__(self):
376 s = '\n'.join(self.comments).rstrip()
376 s = '\n'.join(self.comments).rstrip()
377 if not s:
377 if not s:
378 return ''
378 return ''
379 return s + '\n\n'
379 return s + '\n\n'
380
380
381 def _delmsg(self):
381 def _delmsg(self):
382 '''Remove existing message, keeping the rest of the comments fields.
382 '''Remove existing message, keeping the rest of the comments fields.
383 If comments contains 'subject: ', message will prepend
383 If comments contains 'subject: ', message will prepend
384 the field and a blank line.'''
384 the field and a blank line.'''
385 if self.message:
385 if self.message:
386 subj = 'subject: ' + self.message[0].lower()
386 subj = 'subject: ' + self.message[0].lower()
387 for i in xrange(len(self.comments)):
387 for i in xrange(len(self.comments)):
388 if subj == self.comments[i].lower():
388 if subj == self.comments[i].lower():
389 del self.comments[i]
389 del self.comments[i]
390 self.message = self.message[2:]
390 self.message = self.message[2:]
391 break
391 break
392 ci = 0
392 ci = 0
393 for mi in self.message:
393 for mi in self.message:
394 while mi != self.comments[ci]:
394 while mi != self.comments[ci]:
395 ci += 1
395 ci += 1
396 del self.comments[ci]
396 del self.comments[ci]
397
397
398 def newcommit(repo, phase, *args, **kwargs):
398 def newcommit(repo, phase, *args, **kwargs):
399 """helper dedicated to ensure a commit respect mq.secret setting
399 """helper dedicated to ensure a commit respect mq.secret setting
400
400
401 It should be used instead of repo.commit inside the mq source for operation
401 It should be used instead of repo.commit inside the mq source for operation
402 creating new changeset.
402 creating new changeset.
403 """
403 """
404 repo = repo.unfiltered()
404 repo = repo.unfiltered()
405 if phase is None:
405 if phase is None:
406 if repo.ui.configbool('mq', 'secret', False):
406 if repo.ui.configbool('mq', 'secret', False):
407 phase = phases.secret
407 phase = phases.secret
408 overrides = {('ui', 'allowemptycommit'): True}
408 overrides = {('ui', 'allowemptycommit'): True}
409 if phase is not None:
409 if phase is not None:
410 overrides[('phases', 'new-commit')] = phase
410 overrides[('phases', 'new-commit')] = phase
411 with repo.ui.configoverride(overrides, 'mq'):
411 with repo.ui.configoverride(overrides, 'mq'):
412 repo.ui.setconfig('ui', 'allowemptycommit', True)
412 repo.ui.setconfig('ui', 'allowemptycommit', True)
413 return repo.commit(*args, **kwargs)
413 return repo.commit(*args, **kwargs)
414
414
415 class AbortNoCleanup(error.Abort):
415 class AbortNoCleanup(error.Abort):
416 pass
416 pass
417
417
418 class queue(object):
418 class queue(object):
419 def __init__(self, ui, baseui, path, patchdir=None):
419 def __init__(self, ui, baseui, path, patchdir=None):
420 self.basepath = path
420 self.basepath = path
421 try:
421 try:
422 fh = open(os.path.join(path, 'patches.queue'))
422 fh = open(os.path.join(path, 'patches.queue'))
423 cur = fh.read().rstrip()
423 cur = fh.read().rstrip()
424 fh.close()
424 fh.close()
425 if not cur:
425 if not cur:
426 curpath = os.path.join(path, 'patches')
426 curpath = os.path.join(path, 'patches')
427 else:
427 else:
428 curpath = os.path.join(path, 'patches-' + cur)
428 curpath = os.path.join(path, 'patches-' + cur)
429 except IOError:
429 except IOError:
430 curpath = os.path.join(path, 'patches')
430 curpath = os.path.join(path, 'patches')
431 self.path = patchdir or curpath
431 self.path = patchdir or curpath
432 self.opener = vfsmod.vfs(self.path)
432 self.opener = vfsmod.vfs(self.path)
433 self.ui = ui
433 self.ui = ui
434 self.baseui = baseui
434 self.baseui = baseui
435 self.applieddirty = False
435 self.applieddirty = False
436 self.seriesdirty = False
436 self.seriesdirty = False
437 self.added = []
437 self.added = []
438 self.seriespath = "series"
438 self.seriespath = "series"
439 self.statuspath = "status"
439 self.statuspath = "status"
440 self.guardspath = "guards"
440 self.guardspath = "guards"
441 self.activeguards = None
441 self.activeguards = None
442 self.guardsdirty = False
442 self.guardsdirty = False
443 # Handle mq.git as a bool with extended values
443 # Handle mq.git as a bool with extended values
444 try:
444 try:
445 gitmode = ui.configbool('mq', 'git', None)
445 gitmode = ui.configbool('mq', 'git', None)
446 if gitmode is None:
446 if gitmode is None:
447 raise error.ConfigError
447 raise error.ConfigError
448 if gitmode:
448 if gitmode:
449 self.gitmode = 'yes'
449 self.gitmode = 'yes'
450 else:
450 else:
451 self.gitmode = 'no'
451 self.gitmode = 'no'
452 except error.ConfigError:
452 except error.ConfigError:
453 # let's have check-config ignore the type mismatch
453 # let's have check-config ignore the type mismatch
454 self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
454 self.gitmode = ui.config(r'mq', 'git', 'auto').lower()
455 # deprecated config: mq.plain
455 # deprecated config: mq.plain
456 self.plainmode = ui.configbool('mq', 'plain', False)
456 self.plainmode = ui.configbool('mq', 'plain', False)
457 self.checkapplied = True
457 self.checkapplied = True
458
458
459 @util.propertycache
459 @util.propertycache
460 def applied(self):
460 def applied(self):
461 def parselines(lines):
461 def parselines(lines):
462 for l in lines:
462 for l in lines:
463 entry = l.split(':', 1)
463 entry = l.split(':', 1)
464 if len(entry) > 1:
464 if len(entry) > 1:
465 n, name = entry
465 n, name = entry
466 yield statusentry(bin(n), name)
466 yield statusentry(bin(n), name)
467 elif l.strip():
467 elif l.strip():
468 self.ui.warn(_('malformated mq status line: %s\n') % entry)
468 self.ui.warn(_('malformated mq status line: %s\n') % entry)
469 # else we ignore empty lines
469 # else we ignore empty lines
470 try:
470 try:
471 lines = self.opener.read(self.statuspath).splitlines()
471 lines = self.opener.read(self.statuspath).splitlines()
472 return list(parselines(lines))
472 return list(parselines(lines))
473 except IOError as e:
473 except IOError as e:
474 if e.errno == errno.ENOENT:
474 if e.errno == errno.ENOENT:
475 return []
475 return []
476 raise
476 raise
477
477
478 @util.propertycache
478 @util.propertycache
479 def fullseries(self):
479 def fullseries(self):
480 try:
480 try:
481 return self.opener.read(self.seriespath).splitlines()
481 return self.opener.read(self.seriespath).splitlines()
482 except IOError as e:
482 except IOError as e:
483 if e.errno == errno.ENOENT:
483 if e.errno == errno.ENOENT:
484 return []
484 return []
485 raise
485 raise
486
486
487 @util.propertycache
487 @util.propertycache
488 def series(self):
488 def series(self):
489 self.parseseries()
489 self.parseseries()
490 return self.series
490 return self.series
491
491
492 @util.propertycache
492 @util.propertycache
493 def seriesguards(self):
493 def seriesguards(self):
494 self.parseseries()
494 self.parseseries()
495 return self.seriesguards
495 return self.seriesguards
496
496
497 def invalidate(self):
497 def invalidate(self):
498 for a in 'applied fullseries series seriesguards'.split():
498 for a in 'applied fullseries series seriesguards'.split():
499 if a in self.__dict__:
499 if a in self.__dict__:
500 delattr(self, a)
500 delattr(self, a)
501 self.applieddirty = False
501 self.applieddirty = False
502 self.seriesdirty = False
502 self.seriesdirty = False
503 self.guardsdirty = False
503 self.guardsdirty = False
504 self.activeguards = None
504 self.activeguards = None
505
505
506 def diffopts(self, opts=None, patchfn=None):
506 def diffopts(self, opts=None, patchfn=None):
507 diffopts = patchmod.diffopts(self.ui, opts)
507 diffopts = patchmod.diffopts(self.ui, opts)
508 if self.gitmode == 'auto':
508 if self.gitmode == 'auto':
509 diffopts.upgrade = True
509 diffopts.upgrade = True
510 elif self.gitmode == 'keep':
510 elif self.gitmode == 'keep':
511 pass
511 pass
512 elif self.gitmode in ('yes', 'no'):
512 elif self.gitmode in ('yes', 'no'):
513 diffopts.git = self.gitmode == 'yes'
513 diffopts.git = self.gitmode == 'yes'
514 else:
514 else:
515 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
515 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
516 ' got %s') % self.gitmode)
516 ' got %s') % self.gitmode)
517 if patchfn:
517 if patchfn:
518 diffopts = self.patchopts(diffopts, patchfn)
518 diffopts = self.patchopts(diffopts, patchfn)
519 return diffopts
519 return diffopts
520
520
521 def patchopts(self, diffopts, *patches):
521 def patchopts(self, diffopts, *patches):
522 """Return a copy of input diff options with git set to true if
522 """Return a copy of input diff options with git set to true if
523 referenced patch is a git patch and should be preserved as such.
523 referenced patch is a git patch and should be preserved as such.
524 """
524 """
525 diffopts = diffopts.copy()
525 diffopts = diffopts.copy()
526 if not diffopts.git and self.gitmode == 'keep':
526 if not diffopts.git and self.gitmode == 'keep':
527 for patchfn in patches:
527 for patchfn in patches:
528 patchf = self.opener(patchfn, 'r')
528 patchf = self.opener(patchfn, 'r')
529 # if the patch was a git patch, refresh it as a git patch
529 # if the patch was a git patch, refresh it as a git patch
530 for line in patchf:
530 for line in patchf:
531 if line.startswith('diff --git'):
531 if line.startswith('diff --git'):
532 diffopts.git = True
532 diffopts.git = True
533 break
533 break
534 patchf.close()
534 patchf.close()
535 return diffopts
535 return diffopts
536
536
537 def join(self, *p):
537 def join(self, *p):
538 return os.path.join(self.path, *p)
538 return os.path.join(self.path, *p)
539
539
540 def findseries(self, patch):
540 def findseries(self, patch):
541 def matchpatch(l):
541 def matchpatch(l):
542 l = l.split('#', 1)[0]
542 l = l.split('#', 1)[0]
543 return l.strip() == patch
543 return l.strip() == patch
544 for index, l in enumerate(self.fullseries):
544 for index, l in enumerate(self.fullseries):
545 if matchpatch(l):
545 if matchpatch(l):
546 return index
546 return index
547 return None
547 return None
548
548
549 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
549 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
550
550
551 def parseseries(self):
551 def parseseries(self):
552 self.series = []
552 self.series = []
553 self.seriesguards = []
553 self.seriesguards = []
554 for l in self.fullseries:
554 for l in self.fullseries:
555 h = l.find('#')
555 h = l.find('#')
556 if h == -1:
556 if h == -1:
557 patch = l
557 patch = l
558 comment = ''
558 comment = ''
559 elif h == 0:
559 elif h == 0:
560 continue
560 continue
561 else:
561 else:
562 patch = l[:h]
562 patch = l[:h]
563 comment = l[h:]
563 comment = l[h:]
564 patch = patch.strip()
564 patch = patch.strip()
565 if patch:
565 if patch:
566 if patch in self.series:
566 if patch in self.series:
567 raise error.Abort(_('%s appears more than once in %s') %
567 raise error.Abort(_('%s appears more than once in %s') %
568 (patch, self.join(self.seriespath)))
568 (patch, self.join(self.seriespath)))
569 self.series.append(patch)
569 self.series.append(patch)
570 self.seriesguards.append(self.guard_re.findall(comment))
570 self.seriesguards.append(self.guard_re.findall(comment))
571
571
572 def checkguard(self, guard):
572 def checkguard(self, guard):
573 if not guard:
573 if not guard:
574 return _('guard cannot be an empty string')
574 return _('guard cannot be an empty string')
575 bad_chars = '# \t\r\n\f'
575 bad_chars = '# \t\r\n\f'
576 first = guard[0]
576 first = guard[0]
577 if first in '-+':
577 if first in '-+':
578 return (_('guard %r starts with invalid character: %r') %
578 return (_('guard %r starts with invalid character: %r') %
579 (guard, first))
579 (guard, first))
580 for c in bad_chars:
580 for c in bad_chars:
581 if c in guard:
581 if c in guard:
582 return _('invalid character in guard %r: %r') % (guard, c)
582 return _('invalid character in guard %r: %r') % (guard, c)
583
583
584 def setactive(self, guards):
584 def setactive(self, guards):
585 for guard in guards:
585 for guard in guards:
586 bad = self.checkguard(guard)
586 bad = self.checkguard(guard)
587 if bad:
587 if bad:
588 raise error.Abort(bad)
588 raise error.Abort(bad)
589 guards = sorted(set(guards))
589 guards = sorted(set(guards))
590 self.ui.debug('active guards: %s\n' % ' '.join(guards))
590 self.ui.debug('active guards: %s\n' % ' '.join(guards))
591 self.activeguards = guards
591 self.activeguards = guards
592 self.guardsdirty = True
592 self.guardsdirty = True
593
593
594 def active(self):
594 def active(self):
595 if self.activeguards is None:
595 if self.activeguards is None:
596 self.activeguards = []
596 self.activeguards = []
597 try:
597 try:
598 guards = self.opener.read(self.guardspath).split()
598 guards = self.opener.read(self.guardspath).split()
599 except IOError as err:
599 except IOError as err:
600 if err.errno != errno.ENOENT:
600 if err.errno != errno.ENOENT:
601 raise
601 raise
602 guards = []
602 guards = []
603 for i, guard in enumerate(guards):
603 for i, guard in enumerate(guards):
604 bad = self.checkguard(guard)
604 bad = self.checkguard(guard)
605 if bad:
605 if bad:
606 self.ui.warn('%s:%d: %s\n' %
606 self.ui.warn('%s:%d: %s\n' %
607 (self.join(self.guardspath), i + 1, bad))
607 (self.join(self.guardspath), i + 1, bad))
608 else:
608 else:
609 self.activeguards.append(guard)
609 self.activeguards.append(guard)
610 return self.activeguards
610 return self.activeguards
611
611
612 def setguards(self, idx, guards):
612 def setguards(self, idx, guards):
613 for g in guards:
613 for g in guards:
614 if len(g) < 2:
614 if len(g) < 2:
615 raise error.Abort(_('guard %r too short') % g)
615 raise error.Abort(_('guard %r too short') % g)
616 if g[0] not in '-+':
616 if g[0] not in '-+':
617 raise error.Abort(_('guard %r starts with invalid char') % g)
617 raise error.Abort(_('guard %r starts with invalid char') % g)
618 bad = self.checkguard(g[1:])
618 bad = self.checkguard(g[1:])
619 if bad:
619 if bad:
620 raise error.Abort(bad)
620 raise error.Abort(bad)
621 drop = self.guard_re.sub('', self.fullseries[idx])
621 drop = self.guard_re.sub('', self.fullseries[idx])
622 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
622 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
623 self.parseseries()
623 self.parseseries()
624 self.seriesdirty = True
624 self.seriesdirty = True
625
625
626 def pushable(self, idx):
626 def pushable(self, idx):
627 if isinstance(idx, str):
627 if isinstance(idx, str):
628 idx = self.series.index(idx)
628 idx = self.series.index(idx)
629 patchguards = self.seriesguards[idx]
629 patchguards = self.seriesguards[idx]
630 if not patchguards:
630 if not patchguards:
631 return True, None
631 return True, None
632 guards = self.active()
632 guards = self.active()
633 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
633 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
634 if exactneg:
634 if exactneg:
635 return False, repr(exactneg[0])
635 return False, repr(exactneg[0])
636 pos = [g for g in patchguards if g[0] == '+']
636 pos = [g for g in patchguards if g[0] == '+']
637 exactpos = [g for g in pos if g[1:] in guards]
637 exactpos = [g for g in pos if g[1:] in guards]
638 if pos:
638 if pos:
639 if exactpos:
639 if exactpos:
640 return True, repr(exactpos[0])
640 return True, repr(exactpos[0])
641 return False, ' '.join(map(repr, pos))
641 return False, ' '.join(map(repr, pos))
642 return True, ''
642 return True, ''
643
643
644 def explainpushable(self, idx, all_patches=False):
644 def explainpushable(self, idx, all_patches=False):
645 if all_patches:
645 if all_patches:
646 write = self.ui.write
646 write = self.ui.write
647 else:
647 else:
648 write = self.ui.warn
648 write = self.ui.warn
649
649
650 if all_patches or self.ui.verbose:
650 if all_patches or self.ui.verbose:
651 if isinstance(idx, str):
651 if isinstance(idx, str):
652 idx = self.series.index(idx)
652 idx = self.series.index(idx)
653 pushable, why = self.pushable(idx)
653 pushable, why = self.pushable(idx)
654 if all_patches and pushable:
654 if all_patches and pushable:
655 if why is None:
655 if why is None:
656 write(_('allowing %s - no guards in effect\n') %
656 write(_('allowing %s - no guards in effect\n') %
657 self.series[idx])
657 self.series[idx])
658 else:
658 else:
659 if not why:
659 if not why:
660 write(_('allowing %s - no matching negative guards\n') %
660 write(_('allowing %s - no matching negative guards\n') %
661 self.series[idx])
661 self.series[idx])
662 else:
662 else:
663 write(_('allowing %s - guarded by %s\n') %
663 write(_('allowing %s - guarded by %s\n') %
664 (self.series[idx], why))
664 (self.series[idx], why))
665 if not pushable:
665 if not pushable:
666 if why:
666 if why:
667 write(_('skipping %s - guarded by %s\n') %
667 write(_('skipping %s - guarded by %s\n') %
668 (self.series[idx], why))
668 (self.series[idx], why))
669 else:
669 else:
670 write(_('skipping %s - no matching guards\n') %
670 write(_('skipping %s - no matching guards\n') %
671 self.series[idx])
671 self.series[idx])
672
672
673 def savedirty(self):
673 def savedirty(self):
674 def writelist(items, path):
674 def writelist(items, path):
675 fp = self.opener(path, 'w')
675 fp = self.opener(path, 'w')
676 for i in items:
676 for i in items:
677 fp.write("%s\n" % i)
677 fp.write("%s\n" % i)
678 fp.close()
678 fp.close()
679 if self.applieddirty:
679 if self.applieddirty:
680 writelist(map(str, self.applied), self.statuspath)
680 writelist(map(str, self.applied), self.statuspath)
681 self.applieddirty = False
681 self.applieddirty = False
682 if self.seriesdirty:
682 if self.seriesdirty:
683 writelist(self.fullseries, self.seriespath)
683 writelist(self.fullseries, self.seriespath)
684 self.seriesdirty = False
684 self.seriesdirty = False
685 if self.guardsdirty:
685 if self.guardsdirty:
686 writelist(self.activeguards, self.guardspath)
686 writelist(self.activeguards, self.guardspath)
687 self.guardsdirty = False
687 self.guardsdirty = False
688 if self.added:
688 if self.added:
689 qrepo = self.qrepo()
689 qrepo = self.qrepo()
690 if qrepo:
690 if qrepo:
691 qrepo[None].add(f for f in self.added if f not in qrepo[None])
691 qrepo[None].add(f for f in self.added if f not in qrepo[None])
692 self.added = []
692 self.added = []
693
693
694 def removeundo(self, repo):
694 def removeundo(self, repo):
695 undo = repo.sjoin('undo')
695 undo = repo.sjoin('undo')
696 if not os.path.exists(undo):
696 if not os.path.exists(undo):
697 return
697 return
698 try:
698 try:
699 os.unlink(undo)
699 os.unlink(undo)
700 except OSError as inst:
700 except OSError as inst:
701 self.ui.warn(_('error removing undo: %s\n') % str(inst))
701 self.ui.warn(_('error removing undo: %s\n') % str(inst))
702
702
703 def backup(self, repo, files, copy=False):
703 def backup(self, repo, files, copy=False):
704 # backup local changes in --force case
704 # backup local changes in --force case
705 for f in sorted(files):
705 for f in sorted(files):
706 absf = repo.wjoin(f)
706 absf = repo.wjoin(f)
707 if os.path.lexists(absf):
707 if os.path.lexists(absf):
708 self.ui.note(_('saving current version of %s as %s\n') %
708 self.ui.note(_('saving current version of %s as %s\n') %
709 (f, scmutil.origpath(self.ui, repo, f)))
709 (f, scmutil.origpath(self.ui, repo, f)))
710
710
711 absorig = scmutil.origpath(self.ui, repo, absf)
711 absorig = scmutil.origpath(self.ui, repo, absf)
712 if copy:
712 if copy:
713 util.copyfile(absf, absorig)
713 util.copyfile(absf, absorig)
714 else:
714 else:
715 util.rename(absf, absorig)
715 util.rename(absf, absorig)
716
716
717 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
717 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
718 fp=None, changes=None, opts=None):
718 fp=None, changes=None, opts=None):
719 if opts is None:
719 if opts is None:
720 opts = {}
720 opts = {}
721 stat = opts.get('stat')
721 stat = opts.get('stat')
722 m = scmutil.match(repo[node1], files, opts)
722 m = scmutil.match(repo[node1], files, opts)
723 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
723 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
724 changes, stat, fp)
724 changes, stat, fp)
725
725
726 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
726 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
727 # first try just applying the patch
727 # first try just applying the patch
728 (err, n) = self.apply(repo, [patch], update_status=False,
728 (err, n) = self.apply(repo, [patch], update_status=False,
729 strict=True, merge=rev)
729 strict=True, merge=rev)
730
730
731 if err == 0:
731 if err == 0:
732 return (err, n)
732 return (err, n)
733
733
734 if n is None:
734 if n is None:
735 raise error.Abort(_("apply failed for patch %s") % patch)
735 raise error.Abort(_("apply failed for patch %s") % patch)
736
736
737 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
737 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
738
738
739 # apply failed, strip away that rev and merge.
739 # apply failed, strip away that rev and merge.
740 hg.clean(repo, head)
740 hg.clean(repo, head)
741 strip(self.ui, repo, [n], update=False, backup=False)
741 strip(self.ui, repo, [n], update=False, backup=False)
742
742
743 ctx = repo[rev]
743 ctx = repo[rev]
744 ret = hg.merge(repo, rev)
744 ret = hg.merge(repo, rev)
745 if ret:
745 if ret:
746 raise error.Abort(_("update returned %d") % ret)
746 raise error.Abort(_("update returned %d") % ret)
747 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
747 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
748 if n is None:
748 if n is None:
749 raise error.Abort(_("repo commit failed"))
749 raise error.Abort(_("repo commit failed"))
750 try:
750 try:
751 ph = patchheader(mergeq.join(patch), self.plainmode)
751 ph = patchheader(mergeq.join(patch), self.plainmode)
752 except Exception:
752 except Exception:
753 raise error.Abort(_("unable to read %s") % patch)
753 raise error.Abort(_("unable to read %s") % patch)
754
754
755 diffopts = self.patchopts(diffopts, patch)
755 diffopts = self.patchopts(diffopts, patch)
756 patchf = self.opener(patch, "w")
756 patchf = self.opener(patch, "w")
757 comments = str(ph)
757 comments = str(ph)
758 if comments:
758 if comments:
759 patchf.write(comments)
759 patchf.write(comments)
760 self.printdiff(repo, diffopts, head, n, fp=patchf)
760 self.printdiff(repo, diffopts, head, n, fp=patchf)
761 patchf.close()
761 patchf.close()
762 self.removeundo(repo)
762 self.removeundo(repo)
763 return (0, n)
763 return (0, n)
764
764
765 def qparents(self, repo, rev=None):
765 def qparents(self, repo, rev=None):
766 """return the mq handled parent or p1
766 """return the mq handled parent or p1
767
767
768 In some case where mq get himself in being the parent of a merge the
768 In some case where mq get himself in being the parent of a merge the
769 appropriate parent may be p2.
769 appropriate parent may be p2.
770 (eg: an in progress merge started with mq disabled)
770 (eg: an in progress merge started with mq disabled)
771
771
772 If no parent are managed by mq, p1 is returned.
772 If no parent are managed by mq, p1 is returned.
773 """
773 """
774 if rev is None:
774 if rev is None:
775 (p1, p2) = repo.dirstate.parents()
775 (p1, p2) = repo.dirstate.parents()
776 if p2 == nullid:
776 if p2 == nullid:
777 return p1
777 return p1
778 if not self.applied:
778 if not self.applied:
779 return None
779 return None
780 return self.applied[-1].node
780 return self.applied[-1].node
781 p1, p2 = repo.changelog.parents(rev)
781 p1, p2 = repo.changelog.parents(rev)
782 if p2 != nullid and p2 in [x.node for x in self.applied]:
782 if p2 != nullid and p2 in [x.node for x in self.applied]:
783 return p2
783 return p2
784 return p1
784 return p1
785
785
786 def mergepatch(self, repo, mergeq, series, diffopts):
786 def mergepatch(self, repo, mergeq, series, diffopts):
787 if not self.applied:
787 if not self.applied:
788 # each of the patches merged in will have two parents. This
788 # each of the patches merged in will have two parents. This
789 # can confuse the qrefresh, qdiff, and strip code because it
789 # can confuse the qrefresh, qdiff, and strip code because it
790 # needs to know which parent is actually in the patch queue.
790 # needs to know which parent is actually in the patch queue.
791 # so, we insert a merge marker with only one parent. This way
791 # so, we insert a merge marker with only one parent. This way
792 # the first patch in the queue is never a merge patch
792 # the first patch in the queue is never a merge patch
793 #
793 #
794 pname = ".hg.patches.merge.marker"
794 pname = ".hg.patches.merge.marker"
795 n = newcommit(repo, None, '[mq]: merge marker', force=True)
795 n = newcommit(repo, None, '[mq]: merge marker', force=True)
796 self.removeundo(repo)
796 self.removeundo(repo)
797 self.applied.append(statusentry(n, pname))
797 self.applied.append(statusentry(n, pname))
798 self.applieddirty = True
798 self.applieddirty = True
799
799
800 head = self.qparents(repo)
800 head = self.qparents(repo)
801
801
802 for patch in series:
802 for patch in series:
803 patch = mergeq.lookup(patch, strict=True)
803 patch = mergeq.lookup(patch, strict=True)
804 if not patch:
804 if not patch:
805 self.ui.warn(_("patch %s does not exist\n") % patch)
805 self.ui.warn(_("patch %s does not exist\n") % patch)
806 return (1, None)
806 return (1, None)
807 pushable, reason = self.pushable(patch)
807 pushable, reason = self.pushable(patch)
808 if not pushable:
808 if not pushable:
809 self.explainpushable(patch, all_patches=True)
809 self.explainpushable(patch, all_patches=True)
810 continue
810 continue
811 info = mergeq.isapplied(patch)
811 info = mergeq.isapplied(patch)
812 if not info:
812 if not info:
813 self.ui.warn(_("patch %s is not applied\n") % patch)
813 self.ui.warn(_("patch %s is not applied\n") % patch)
814 return (1, None)
814 return (1, None)
815 rev = info[1]
815 rev = info[1]
816 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
816 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
817 if head:
817 if head:
818 self.applied.append(statusentry(head, patch))
818 self.applied.append(statusentry(head, patch))
819 self.applieddirty = True
819 self.applieddirty = True
820 if err:
820 if err:
821 return (err, head)
821 return (err, head)
822 self.savedirty()
822 self.savedirty()
823 return (0, head)
823 return (0, head)
824
824
825 def patch(self, repo, patchfile):
825 def patch(self, repo, patchfile):
826 '''Apply patchfile to the working directory.
826 '''Apply patchfile to the working directory.
827 patchfile: name of patch file'''
827 patchfile: name of patch file'''
828 files = set()
828 files = set()
829 try:
829 try:
830 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
830 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
831 files=files, eolmode=None)
831 files=files, eolmode=None)
832 return (True, list(files), fuzz)
832 return (True, list(files), fuzz)
833 except Exception as inst:
833 except Exception as inst:
834 self.ui.note(str(inst) + '\n')
834 self.ui.note(str(inst) + '\n')
835 if not self.ui.verbose:
835 if not self.ui.verbose:
836 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
836 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
837 self.ui.traceback()
837 self.ui.traceback()
838 return (False, list(files), False)
838 return (False, list(files), False)
839
839
840 def apply(self, repo, series, list=False, update_status=True,
840 def apply(self, repo, series, list=False, update_status=True,
841 strict=False, patchdir=None, merge=None, all_files=None,
841 strict=False, patchdir=None, merge=None, all_files=None,
842 tobackup=None, keepchanges=False):
842 tobackup=None, keepchanges=False):
843 wlock = lock = tr = None
843 wlock = lock = tr = None
844 try:
844 try:
845 wlock = repo.wlock()
845 wlock = repo.wlock()
846 lock = repo.lock()
846 lock = repo.lock()
847 tr = repo.transaction("qpush")
847 tr = repo.transaction("qpush")
848 try:
848 try:
849 ret = self._apply(repo, series, list, update_status,
849 ret = self._apply(repo, series, list, update_status,
850 strict, patchdir, merge, all_files=all_files,
850 strict, patchdir, merge, all_files=all_files,
851 tobackup=tobackup, keepchanges=keepchanges)
851 tobackup=tobackup, keepchanges=keepchanges)
852 tr.close()
852 tr.close()
853 self.savedirty()
853 self.savedirty()
854 return ret
854 return ret
855 except AbortNoCleanup:
855 except AbortNoCleanup:
856 tr.close()
856 tr.close()
857 self.savedirty()
857 self.savedirty()
858 raise
858 raise
859 except: # re-raises
859 except: # re-raises
860 try:
860 try:
861 tr.abort()
861 tr.abort()
862 finally:
862 finally:
863 self.invalidate()
863 self.invalidate()
864 raise
864 raise
865 finally:
865 finally:
866 release(tr, lock, wlock)
866 release(tr, lock, wlock)
867 self.removeundo(repo)
867 self.removeundo(repo)
868
868
869 def _apply(self, repo, series, list=False, update_status=True,
869 def _apply(self, repo, series, list=False, update_status=True,
870 strict=False, patchdir=None, merge=None, all_files=None,
870 strict=False, patchdir=None, merge=None, all_files=None,
871 tobackup=None, keepchanges=False):
871 tobackup=None, keepchanges=False):
872 """returns (error, hash)
872 """returns (error, hash)
873
873
874 error = 1 for unable to read, 2 for patch failed, 3 for patch
874 error = 1 for unable to read, 2 for patch failed, 3 for patch
875 fuzz. tobackup is None or a set of files to backup before they
875 fuzz. tobackup is None or a set of files to backup before they
876 are modified by a patch.
876 are modified by a patch.
877 """
877 """
878 # TODO unify with commands.py
878 # TODO unify with commands.py
879 if not patchdir:
879 if not patchdir:
880 patchdir = self.path
880 patchdir = self.path
881 err = 0
881 err = 0
882 n = None
882 n = None
883 for patchname in series:
883 for patchname in series:
884 pushable, reason = self.pushable(patchname)
884 pushable, reason = self.pushable(patchname)
885 if not pushable:
885 if not pushable:
886 self.explainpushable(patchname, all_patches=True)
886 self.explainpushable(patchname, all_patches=True)
887 continue
887 continue
888 self.ui.status(_("applying %s\n") % patchname)
888 self.ui.status(_("applying %s\n") % patchname)
889 pf = os.path.join(patchdir, patchname)
889 pf = os.path.join(patchdir, patchname)
890
890
891 try:
891 try:
892 ph = patchheader(self.join(patchname), self.plainmode)
892 ph = patchheader(self.join(patchname), self.plainmode)
893 except IOError:
893 except IOError:
894 self.ui.warn(_("unable to read %s\n") % patchname)
894 self.ui.warn(_("unable to read %s\n") % patchname)
895 err = 1
895 err = 1
896 break
896 break
897
897
898 message = ph.message
898 message = ph.message
899 if not message:
899 if not message:
900 # The commit message should not be translated
900 # The commit message should not be translated
901 message = "imported patch %s\n" % patchname
901 message = "imported patch %s\n" % patchname
902 else:
902 else:
903 if list:
903 if list:
904 # The commit message should not be translated
904 # The commit message should not be translated
905 message.append("\nimported patch %s" % patchname)
905 message.append("\nimported patch %s" % patchname)
906 message = '\n'.join(message)
906 message = '\n'.join(message)
907
907
908 if ph.haspatch:
908 if ph.haspatch:
909 if tobackup:
909 if tobackup:
910 touched = patchmod.changedfiles(self.ui, repo, pf)
910 touched = patchmod.changedfiles(self.ui, repo, pf)
911 touched = set(touched) & tobackup
911 touched = set(touched) & tobackup
912 if touched and keepchanges:
912 if touched and keepchanges:
913 raise AbortNoCleanup(
913 raise AbortNoCleanup(
914 _("conflicting local changes found"),
914 _("conflicting local changes found"),
915 hint=_("did you forget to qrefresh?"))
915 hint=_("did you forget to qrefresh?"))
916 self.backup(repo, touched, copy=True)
916 self.backup(repo, touched, copy=True)
917 tobackup = tobackup - touched
917 tobackup = tobackup - touched
918 (patcherr, files, fuzz) = self.patch(repo, pf)
918 (patcherr, files, fuzz) = self.patch(repo, pf)
919 if all_files is not None:
919 if all_files is not None:
920 all_files.update(files)
920 all_files.update(files)
921 patcherr = not patcherr
921 patcherr = not patcherr
922 else:
922 else:
923 self.ui.warn(_("patch %s is empty\n") % patchname)
923 self.ui.warn(_("patch %s is empty\n") % patchname)
924 patcherr, files, fuzz = 0, [], 0
924 patcherr, files, fuzz = 0, [], 0
925
925
926 if merge and files:
926 if merge and files:
927 # Mark as removed/merged and update dirstate parent info
927 # Mark as removed/merged and update dirstate parent info
928 removed = []
928 removed = []
929 merged = []
929 merged = []
930 for f in files:
930 for f in files:
931 if os.path.lexists(repo.wjoin(f)):
931 if os.path.lexists(repo.wjoin(f)):
932 merged.append(f)
932 merged.append(f)
933 else:
933 else:
934 removed.append(f)
934 removed.append(f)
935 repo.dirstate.beginparentchange()
935 repo.dirstate.beginparentchange()
936 for f in removed:
936 for f in removed:
937 repo.dirstate.remove(f)
937 repo.dirstate.remove(f)
938 for f in merged:
938 for f in merged:
939 repo.dirstate.merge(f)
939 repo.dirstate.merge(f)
940 p1, p2 = repo.dirstate.parents()
940 p1, p2 = repo.dirstate.parents()
941 repo.setparents(p1, merge)
941 repo.setparents(p1, merge)
942 repo.dirstate.endparentchange()
942 repo.dirstate.endparentchange()
943
943
944 if all_files and '.hgsubstate' in all_files:
944 if all_files and '.hgsubstate' in all_files:
945 wctx = repo[None]
945 wctx = repo[None]
946 pctx = repo['.']
946 pctx = repo['.']
947 overwrite = False
947 overwrite = False
948 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
948 mergedsubstate = subrepo.submerge(repo, pctx, wctx, wctx,
949 overwrite)
949 overwrite)
950 files += mergedsubstate.keys()
950 files += mergedsubstate.keys()
951
951
952 match = scmutil.matchfiles(repo, files or [])
952 match = scmutil.matchfiles(repo, files or [])
953 oldtip = repo['tip']
953 oldtip = repo['tip']
954 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
954 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
955 force=True)
955 force=True)
956 if repo['tip'] == oldtip:
956 if repo['tip'] == oldtip:
957 raise error.Abort(_("qpush exactly duplicates child changeset"))
957 raise error.Abort(_("qpush exactly duplicates child changeset"))
958 if n is None:
958 if n is None:
959 raise error.Abort(_("repository commit failed"))
959 raise error.Abort(_("repository commit failed"))
960
960
961 if update_status:
961 if update_status:
962 self.applied.append(statusentry(n, patchname))
962 self.applied.append(statusentry(n, patchname))
963
963
964 if patcherr:
964 if patcherr:
965 self.ui.warn(_("patch failed, rejects left in working "
965 self.ui.warn(_("patch failed, rejects left in working "
966 "directory\n"))
966 "directory\n"))
967 err = 2
967 err = 2
968 break
968 break
969
969
970 if fuzz and strict:
970 if fuzz and strict:
971 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
971 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
972 err = 3
972 err = 3
973 break
973 break
974 return (err, n)
974 return (err, n)
975
975
976 def _cleanup(self, patches, numrevs, keep=False):
976 def _cleanup(self, patches, numrevs, keep=False):
977 if not keep:
977 if not keep:
978 r = self.qrepo()
978 r = self.qrepo()
979 if r:
979 if r:
980 r[None].forget(patches)
980 r[None].forget(patches)
981 for p in patches:
981 for p in patches:
982 try:
982 try:
983 os.unlink(self.join(p))
983 os.unlink(self.join(p))
984 except OSError as inst:
984 except OSError as inst:
985 if inst.errno != errno.ENOENT:
985 if inst.errno != errno.ENOENT:
986 raise
986 raise
987
987
988 qfinished = []
988 qfinished = []
989 if numrevs:
989 if numrevs:
990 qfinished = self.applied[:numrevs]
990 qfinished = self.applied[:numrevs]
991 del self.applied[:numrevs]
991 del self.applied[:numrevs]
992 self.applieddirty = True
992 self.applieddirty = True
993
993
994 unknown = []
994 unknown = []
995
995
996 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
996 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
997 reverse=True):
997 reverse=True):
998 if i is not None:
998 if i is not None:
999 del self.fullseries[i]
999 del self.fullseries[i]
1000 else:
1000 else:
1001 unknown.append(p)
1001 unknown.append(p)
1002
1002
1003 if unknown:
1003 if unknown:
1004 if numrevs:
1004 if numrevs:
1005 rev = dict((entry.name, entry.node) for entry in qfinished)
1005 rev = dict((entry.name, entry.node) for entry in qfinished)
1006 for p in unknown:
1006 for p in unknown:
1007 msg = _('revision %s refers to unknown patches: %s\n')
1007 msg = _('revision %s refers to unknown patches: %s\n')
1008 self.ui.warn(msg % (short(rev[p]), p))
1008 self.ui.warn(msg % (short(rev[p]), p))
1009 else:
1009 else:
1010 msg = _('unknown patches: %s\n')
1010 msg = _('unknown patches: %s\n')
1011 raise error.Abort(''.join(msg % p for p in unknown))
1011 raise error.Abort(''.join(msg % p for p in unknown))
1012
1012
1013 self.parseseries()
1013 self.parseseries()
1014 self.seriesdirty = True
1014 self.seriesdirty = True
1015 return [entry.node for entry in qfinished]
1015 return [entry.node for entry in qfinished]
1016
1016
1017 def _revpatches(self, repo, revs):
1017 def _revpatches(self, repo, revs):
1018 firstrev = repo[self.applied[0].node].rev()
1018 firstrev = repo[self.applied[0].node].rev()
1019 patches = []
1019 patches = []
1020 for i, rev in enumerate(revs):
1020 for i, rev in enumerate(revs):
1021
1021
1022 if rev < firstrev:
1022 if rev < firstrev:
1023 raise error.Abort(_('revision %d is not managed') % rev)
1023 raise error.Abort(_('revision %d is not managed') % rev)
1024
1024
1025 ctx = repo[rev]
1025 ctx = repo[rev]
1026 base = self.applied[i].node
1026 base = self.applied[i].node
1027 if ctx.node() != base:
1027 if ctx.node() != base:
1028 msg = _('cannot delete revision %d above applied patches')
1028 msg = _('cannot delete revision %d above applied patches')
1029 raise error.Abort(msg % rev)
1029 raise error.Abort(msg % rev)
1030
1030
1031 patch = self.applied[i].name
1031 patch = self.applied[i].name
1032 for fmt in ('[mq]: %s', 'imported patch %s'):
1032 for fmt in ('[mq]: %s', 'imported patch %s'):
1033 if ctx.description() == fmt % patch:
1033 if ctx.description() == fmt % patch:
1034 msg = _('patch %s finalized without changeset message\n')
1034 msg = _('patch %s finalized without changeset message\n')
1035 repo.ui.status(msg % patch)
1035 repo.ui.status(msg % patch)
1036 break
1036 break
1037
1037
1038 patches.append(patch)
1038 patches.append(patch)
1039 return patches
1039 return patches
1040
1040
1041 def finish(self, repo, revs):
1041 def finish(self, repo, revs):
1042 # Manually trigger phase computation to ensure phasedefaults is
1042 # Manually trigger phase computation to ensure phasedefaults is
1043 # executed before we remove the patches.
1043 # executed before we remove the patches.
1044 repo._phasecache
1044 repo._phasecache
1045 patches = self._revpatches(repo, sorted(revs))
1045 patches = self._revpatches(repo, sorted(revs))
1046 qfinished = self._cleanup(patches, len(patches))
1046 qfinished = self._cleanup(patches, len(patches))
1047 if qfinished and repo.ui.configbool('mq', 'secret', False):
1047 if qfinished and repo.ui.configbool('mq', 'secret', False):
1048 # only use this logic when the secret option is added
1048 # only use this logic when the secret option is added
1049 oldqbase = repo[qfinished[0]]
1049 oldqbase = repo[qfinished[0]]
1050 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
1050 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
1051 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1051 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1052 with repo.transaction('qfinish') as tr:
1052 with repo.transaction('qfinish') as tr:
1053 phases.advanceboundary(repo, tr, tphase, qfinished)
1053 phases.advanceboundary(repo, tr, tphase, qfinished)
1054
1054
1055 def delete(self, repo, patches, opts):
1055 def delete(self, repo, patches, opts):
1056 if not patches and not opts.get('rev'):
1056 if not patches and not opts.get('rev'):
1057 raise error.Abort(_('qdelete requires at least one revision or '
1057 raise error.Abort(_('qdelete requires at least one revision or '
1058 'patch name'))
1058 'patch name'))
1059
1059
1060 realpatches = []
1060 realpatches = []
1061 for patch in patches:
1061 for patch in patches:
1062 patch = self.lookup(patch, strict=True)
1062 patch = self.lookup(patch, strict=True)
1063 info = self.isapplied(patch)
1063 info = self.isapplied(patch)
1064 if info:
1064 if info:
1065 raise error.Abort(_("cannot delete applied patch %s") % patch)
1065 raise error.Abort(_("cannot delete applied patch %s") % patch)
1066 if patch not in self.series:
1066 if patch not in self.series:
1067 raise error.Abort(_("patch %s not in series file") % patch)
1067 raise error.Abort(_("patch %s not in series file") % patch)
1068 if patch not in realpatches:
1068 if patch not in realpatches:
1069 realpatches.append(patch)
1069 realpatches.append(patch)
1070
1070
1071 numrevs = 0
1071 numrevs = 0
1072 if opts.get('rev'):
1072 if opts.get('rev'):
1073 if not self.applied:
1073 if not self.applied:
1074 raise error.Abort(_('no patches applied'))
1074 raise error.Abort(_('no patches applied'))
1075 revs = scmutil.revrange(repo, opts.get('rev'))
1075 revs = scmutil.revrange(repo, opts.get('rev'))
1076 revs.sort()
1076 revs.sort()
1077 revpatches = self._revpatches(repo, revs)
1077 revpatches = self._revpatches(repo, revs)
1078 realpatches += revpatches
1078 realpatches += revpatches
1079 numrevs = len(revpatches)
1079 numrevs = len(revpatches)
1080
1080
1081 self._cleanup(realpatches, numrevs, opts.get('keep'))
1081 self._cleanup(realpatches, numrevs, opts.get('keep'))
1082
1082
1083 def checktoppatch(self, repo):
1083 def checktoppatch(self, repo):
1084 '''check that working directory is at qtip'''
1084 '''check that working directory is at qtip'''
1085 if self.applied:
1085 if self.applied:
1086 top = self.applied[-1].node
1086 top = self.applied[-1].node
1087 patch = self.applied[-1].name
1087 patch = self.applied[-1].name
1088 if repo.dirstate.p1() != top:
1088 if repo.dirstate.p1() != top:
1089 raise error.Abort(_("working directory revision is not qtip"))
1089 raise error.Abort(_("working directory revision is not qtip"))
1090 return top, patch
1090 return top, patch
1091 return None, None
1091 return None, None
1092
1092
1093 def putsubstate2changes(self, substatestate, changes):
1093 def putsubstate2changes(self, substatestate, changes):
1094 for files in changes[:3]:
1094 for files in changes[:3]:
1095 if '.hgsubstate' in files:
1095 if '.hgsubstate' in files:
1096 return # already listed up
1096 return # already listed up
1097 # not yet listed up
1097 # not yet listed up
1098 if substatestate in 'a?':
1098 if substatestate in 'a?':
1099 changes[1].append('.hgsubstate')
1099 changes[1].append('.hgsubstate')
1100 elif substatestate in 'r':
1100 elif substatestate in 'r':
1101 changes[2].append('.hgsubstate')
1101 changes[2].append('.hgsubstate')
1102 else: # modified
1102 else: # modified
1103 changes[0].append('.hgsubstate')
1103 changes[0].append('.hgsubstate')
1104
1104
1105 def checklocalchanges(self, repo, force=False, refresh=True):
1105 def checklocalchanges(self, repo, force=False, refresh=True):
1106 excsuffix = ''
1106 excsuffix = ''
1107 if refresh:
1107 if refresh:
1108 excsuffix = ', qrefresh first'
1108 excsuffix = ', qrefresh first'
1109 # plain versions for i18n tool to detect them
1109 # plain versions for i18n tool to detect them
1110 _("local changes found, qrefresh first")
1110 _("local changes found, qrefresh first")
1111 _("local changed subrepos found, qrefresh first")
1111 _("local changed subrepos found, qrefresh first")
1112 return checklocalchanges(repo, force, excsuffix)
1112 return checklocalchanges(repo, force, excsuffix)
1113
1113
1114 _reserved = ('series', 'status', 'guards', '.', '..')
1114 _reserved = ('series', 'status', 'guards', '.', '..')
1115 def checkreservedname(self, name):
1115 def checkreservedname(self, name):
1116 if name in self._reserved:
1116 if name in self._reserved:
1117 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1117 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1118 % name)
1118 % name)
1119 if name != name.strip():
1120 # whitespace is stripped by parseseries()
1121 raise error.Abort(_('patch name cannot begin or end with '
1122 'whitespace'))
1119 for prefix in ('.hg', '.mq'):
1123 for prefix in ('.hg', '.mq'):
1120 if name.startswith(prefix):
1124 if name.startswith(prefix):
1121 raise error.Abort(_('patch name cannot begin with "%s"')
1125 raise error.Abort(_('patch name cannot begin with "%s"')
1122 % prefix)
1126 % prefix)
1123 for c in ('#', ':', '\r', '\n'):
1127 for c in ('#', ':', '\r', '\n'):
1124 if c in name:
1128 if c in name:
1125 raise error.Abort(_('%r cannot be used in the name of a patch')
1129 raise error.Abort(_('%r cannot be used in the name of a patch')
1126 % c)
1130 % c)
1127
1131
1128 def checkpatchname(self, name, force=False):
1132 def checkpatchname(self, name, force=False):
1129 self.checkreservedname(name)
1133 self.checkreservedname(name)
1130 if not force and os.path.exists(self.join(name)):
1134 if not force and os.path.exists(self.join(name)):
1131 if os.path.isdir(self.join(name)):
1135 if os.path.isdir(self.join(name)):
1132 raise error.Abort(_('"%s" already exists as a directory')
1136 raise error.Abort(_('"%s" already exists as a directory')
1133 % name)
1137 % name)
1134 else:
1138 else:
1135 raise error.Abort(_('patch "%s" already exists') % name)
1139 raise error.Abort(_('patch "%s" already exists') % name)
1136
1140
1137 def makepatchname(self, title, fallbackname):
1141 def makepatchname(self, title, fallbackname):
1138 """Return a suitable filename for title, adding a suffix to make
1142 """Return a suitable filename for title, adding a suffix to make
1139 it unique in the existing list"""
1143 it unique in the existing list"""
1140 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
1144 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
1141 namebase = namebase[:75] # avoid too long name (issue5117)
1145 namebase = namebase[:75] # avoid too long name (issue5117)
1142 if namebase:
1146 if namebase:
1143 try:
1147 try:
1144 self.checkreservedname(namebase)
1148 self.checkreservedname(namebase)
1145 except error.Abort:
1149 except error.Abort:
1146 namebase = fallbackname
1150 namebase = fallbackname
1147 else:
1151 else:
1148 namebase = fallbackname
1152 namebase = fallbackname
1149 name = namebase
1153 name = namebase
1150 i = 0
1154 i = 0
1151 while True:
1155 while True:
1152 if name not in self.fullseries:
1156 if name not in self.fullseries:
1153 try:
1157 try:
1154 self.checkpatchname(name)
1158 self.checkpatchname(name)
1155 break
1159 break
1156 except error.Abort:
1160 except error.Abort:
1157 pass
1161 pass
1158 i += 1
1162 i += 1
1159 name = '%s__%s' % (namebase, i)
1163 name = '%s__%s' % (namebase, i)
1160 return name
1164 return name
1161
1165
1162 def checkkeepchanges(self, keepchanges, force):
1166 def checkkeepchanges(self, keepchanges, force):
1163 if force and keepchanges:
1167 if force and keepchanges:
1164 raise error.Abort(_('cannot use both --force and --keep-changes'))
1168 raise error.Abort(_('cannot use both --force and --keep-changes'))
1165
1169
1166 def new(self, repo, patchfn, *pats, **opts):
1170 def new(self, repo, patchfn, *pats, **opts):
1167 """options:
1171 """options:
1168 msg: a string or a no-argument function returning a string
1172 msg: a string or a no-argument function returning a string
1169 """
1173 """
1170 msg = opts.get('msg')
1174 msg = opts.get('msg')
1171 edit = opts.get('edit')
1175 edit = opts.get('edit')
1172 editform = opts.get('editform', 'mq.qnew')
1176 editform = opts.get('editform', 'mq.qnew')
1173 user = opts.get('user')
1177 user = opts.get('user')
1174 date = opts.get('date')
1178 date = opts.get('date')
1175 if date:
1179 if date:
1176 date = util.parsedate(date)
1180 date = util.parsedate(date)
1177 diffopts = self.diffopts({'git': opts.get('git')})
1181 diffopts = self.diffopts({'git': opts.get('git')})
1178 if opts.get('checkname', True):
1182 if opts.get('checkname', True):
1179 self.checkpatchname(patchfn)
1183 self.checkpatchname(patchfn)
1180 inclsubs = checksubstate(repo)
1184 inclsubs = checksubstate(repo)
1181 if inclsubs:
1185 if inclsubs:
1182 substatestate = repo.dirstate['.hgsubstate']
1186 substatestate = repo.dirstate['.hgsubstate']
1183 if opts.get('include') or opts.get('exclude') or pats:
1187 if opts.get('include') or opts.get('exclude') or pats:
1184 # detect missing files in pats
1188 # detect missing files in pats
1185 def badfn(f, msg):
1189 def badfn(f, msg):
1186 if f != '.hgsubstate': # .hgsubstate is auto-created
1190 if f != '.hgsubstate': # .hgsubstate is auto-created
1187 raise error.Abort('%s: %s' % (f, msg))
1191 raise error.Abort('%s: %s' % (f, msg))
1188 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1192 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1189 changes = repo.status(match=match)
1193 changes = repo.status(match=match)
1190 else:
1194 else:
1191 changes = self.checklocalchanges(repo, force=True)
1195 changes = self.checklocalchanges(repo, force=True)
1192 commitfiles = list(inclsubs)
1196 commitfiles = list(inclsubs)
1193 for files in changes[:3]:
1197 for files in changes[:3]:
1194 commitfiles.extend(files)
1198 commitfiles.extend(files)
1195 match = scmutil.matchfiles(repo, commitfiles)
1199 match = scmutil.matchfiles(repo, commitfiles)
1196 if len(repo[None].parents()) > 1:
1200 if len(repo[None].parents()) > 1:
1197 raise error.Abort(_('cannot manage merge changesets'))
1201 raise error.Abort(_('cannot manage merge changesets'))
1198 self.checktoppatch(repo)
1202 self.checktoppatch(repo)
1199 insert = self.fullseriesend()
1203 insert = self.fullseriesend()
1200 with repo.wlock():
1204 with repo.wlock():
1201 try:
1205 try:
1202 # if patch file write fails, abort early
1206 # if patch file write fails, abort early
1203 p = self.opener(patchfn, "w")
1207 p = self.opener(patchfn, "w")
1204 except IOError as e:
1208 except IOError as e:
1205 raise error.Abort(_('cannot write patch "%s": %s')
1209 raise error.Abort(_('cannot write patch "%s": %s')
1206 % (patchfn, e.strerror))
1210 % (patchfn, e.strerror))
1207 try:
1211 try:
1208 defaultmsg = "[mq]: %s" % patchfn
1212 defaultmsg = "[mq]: %s" % patchfn
1209 editor = cmdutil.getcommiteditor(editform=editform)
1213 editor = cmdutil.getcommiteditor(editform=editform)
1210 if edit:
1214 if edit:
1211 def finishdesc(desc):
1215 def finishdesc(desc):
1212 if desc.rstrip():
1216 if desc.rstrip():
1213 return desc
1217 return desc
1214 else:
1218 else:
1215 return defaultmsg
1219 return defaultmsg
1216 # i18n: this message is shown in editor with "HG: " prefix
1220 # i18n: this message is shown in editor with "HG: " prefix
1217 extramsg = _('Leave message empty to use default message.')
1221 extramsg = _('Leave message empty to use default message.')
1218 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1222 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1219 extramsg=extramsg,
1223 extramsg=extramsg,
1220 editform=editform)
1224 editform=editform)
1221 commitmsg = msg
1225 commitmsg = msg
1222 else:
1226 else:
1223 commitmsg = msg or defaultmsg
1227 commitmsg = msg or defaultmsg
1224
1228
1225 n = newcommit(repo, None, commitmsg, user, date, match=match,
1229 n = newcommit(repo, None, commitmsg, user, date, match=match,
1226 force=True, editor=editor)
1230 force=True, editor=editor)
1227 if n is None:
1231 if n is None:
1228 raise error.Abort(_("repo commit failed"))
1232 raise error.Abort(_("repo commit failed"))
1229 try:
1233 try:
1230 self.fullseries[insert:insert] = [patchfn]
1234 self.fullseries[insert:insert] = [patchfn]
1231 self.applied.append(statusentry(n, patchfn))
1235 self.applied.append(statusentry(n, patchfn))
1232 self.parseseries()
1236 self.parseseries()
1233 self.seriesdirty = True
1237 self.seriesdirty = True
1234 self.applieddirty = True
1238 self.applieddirty = True
1235 nctx = repo[n]
1239 nctx = repo[n]
1236 ph = patchheader(self.join(patchfn), self.plainmode)
1240 ph = patchheader(self.join(patchfn), self.plainmode)
1237 if user:
1241 if user:
1238 ph.setuser(user)
1242 ph.setuser(user)
1239 if date:
1243 if date:
1240 ph.setdate('%s %s' % date)
1244 ph.setdate('%s %s' % date)
1241 ph.setparent(hex(nctx.p1().node()))
1245 ph.setparent(hex(nctx.p1().node()))
1242 msg = nctx.description().strip()
1246 msg = nctx.description().strip()
1243 if msg == defaultmsg.strip():
1247 if msg == defaultmsg.strip():
1244 msg = ''
1248 msg = ''
1245 ph.setmessage(msg)
1249 ph.setmessage(msg)
1246 p.write(str(ph))
1250 p.write(str(ph))
1247 if commitfiles:
1251 if commitfiles:
1248 parent = self.qparents(repo, n)
1252 parent = self.qparents(repo, n)
1249 if inclsubs:
1253 if inclsubs:
1250 self.putsubstate2changes(substatestate, changes)
1254 self.putsubstate2changes(substatestate, changes)
1251 chunks = patchmod.diff(repo, node1=parent, node2=n,
1255 chunks = patchmod.diff(repo, node1=parent, node2=n,
1252 changes=changes, opts=diffopts)
1256 changes=changes, opts=diffopts)
1253 for chunk in chunks:
1257 for chunk in chunks:
1254 p.write(chunk)
1258 p.write(chunk)
1255 p.close()
1259 p.close()
1256 r = self.qrepo()
1260 r = self.qrepo()
1257 if r:
1261 if r:
1258 r[None].add([patchfn])
1262 r[None].add([patchfn])
1259 except: # re-raises
1263 except: # re-raises
1260 repo.rollback()
1264 repo.rollback()
1261 raise
1265 raise
1262 except Exception:
1266 except Exception:
1263 patchpath = self.join(patchfn)
1267 patchpath = self.join(patchfn)
1264 try:
1268 try:
1265 os.unlink(patchpath)
1269 os.unlink(patchpath)
1266 except OSError:
1270 except OSError:
1267 self.ui.warn(_('error unlinking %s\n') % patchpath)
1271 self.ui.warn(_('error unlinking %s\n') % patchpath)
1268 raise
1272 raise
1269 self.removeundo(repo)
1273 self.removeundo(repo)
1270
1274
1271 def isapplied(self, patch):
1275 def isapplied(self, patch):
1272 """returns (index, rev, patch)"""
1276 """returns (index, rev, patch)"""
1273 for i, a in enumerate(self.applied):
1277 for i, a in enumerate(self.applied):
1274 if a.name == patch:
1278 if a.name == patch:
1275 return (i, a.node, a.name)
1279 return (i, a.node, a.name)
1276 return None
1280 return None
1277
1281
1278 # if the exact patch name does not exist, we try a few
1282 # if the exact patch name does not exist, we try a few
1279 # variations. If strict is passed, we try only #1
1283 # variations. If strict is passed, we try only #1
1280 #
1284 #
1281 # 1) a number (as string) to indicate an offset in the series file
1285 # 1) a number (as string) to indicate an offset in the series file
1282 # 2) a unique substring of the patch name was given
1286 # 2) a unique substring of the patch name was given
1283 # 3) patchname[-+]num to indicate an offset in the series file
1287 # 3) patchname[-+]num to indicate an offset in the series file
1284 def lookup(self, patch, strict=False):
1288 def lookup(self, patch, strict=False):
1285 def partialname(s):
1289 def partialname(s):
1286 if s in self.series:
1290 if s in self.series:
1287 return s
1291 return s
1288 matches = [x for x in self.series if s in x]
1292 matches = [x for x in self.series if s in x]
1289 if len(matches) > 1:
1293 if len(matches) > 1:
1290 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1294 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1291 for m in matches:
1295 for m in matches:
1292 self.ui.warn(' %s\n' % m)
1296 self.ui.warn(' %s\n' % m)
1293 return None
1297 return None
1294 if matches:
1298 if matches:
1295 return matches[0]
1299 return matches[0]
1296 if self.series and self.applied:
1300 if self.series and self.applied:
1297 if s == 'qtip':
1301 if s == 'qtip':
1298 return self.series[self.seriesend(True) - 1]
1302 return self.series[self.seriesend(True) - 1]
1299 if s == 'qbase':
1303 if s == 'qbase':
1300 return self.series[0]
1304 return self.series[0]
1301 return None
1305 return None
1302
1306
1303 if patch in self.series:
1307 if patch in self.series:
1304 return patch
1308 return patch
1305
1309
1306 if not os.path.isfile(self.join(patch)):
1310 if not os.path.isfile(self.join(patch)):
1307 try:
1311 try:
1308 sno = int(patch)
1312 sno = int(patch)
1309 except (ValueError, OverflowError):
1313 except (ValueError, OverflowError):
1310 pass
1314 pass
1311 else:
1315 else:
1312 if -len(self.series) <= sno < len(self.series):
1316 if -len(self.series) <= sno < len(self.series):
1313 return self.series[sno]
1317 return self.series[sno]
1314
1318
1315 if not strict:
1319 if not strict:
1316 res = partialname(patch)
1320 res = partialname(patch)
1317 if res:
1321 if res:
1318 return res
1322 return res
1319 minus = patch.rfind('-')
1323 minus = patch.rfind('-')
1320 if minus >= 0:
1324 if minus >= 0:
1321 res = partialname(patch[:minus])
1325 res = partialname(patch[:minus])
1322 if res:
1326 if res:
1323 i = self.series.index(res)
1327 i = self.series.index(res)
1324 try:
1328 try:
1325 off = int(patch[minus + 1:] or 1)
1329 off = int(patch[minus + 1:] or 1)
1326 except (ValueError, OverflowError):
1330 except (ValueError, OverflowError):
1327 pass
1331 pass
1328 else:
1332 else:
1329 if i - off >= 0:
1333 if i - off >= 0:
1330 return self.series[i - off]
1334 return self.series[i - off]
1331 plus = patch.rfind('+')
1335 plus = patch.rfind('+')
1332 if plus >= 0:
1336 if plus >= 0:
1333 res = partialname(patch[:plus])
1337 res = partialname(patch[:plus])
1334 if res:
1338 if res:
1335 i = self.series.index(res)
1339 i = self.series.index(res)
1336 try:
1340 try:
1337 off = int(patch[plus + 1:] or 1)
1341 off = int(patch[plus + 1:] or 1)
1338 except (ValueError, OverflowError):
1342 except (ValueError, OverflowError):
1339 pass
1343 pass
1340 else:
1344 else:
1341 if i + off < len(self.series):
1345 if i + off < len(self.series):
1342 return self.series[i + off]
1346 return self.series[i + off]
1343 raise error.Abort(_("patch %s not in series") % patch)
1347 raise error.Abort(_("patch %s not in series") % patch)
1344
1348
1345 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1349 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1346 all=False, move=False, exact=False, nobackup=False,
1350 all=False, move=False, exact=False, nobackup=False,
1347 keepchanges=False):
1351 keepchanges=False):
1348 self.checkkeepchanges(keepchanges, force)
1352 self.checkkeepchanges(keepchanges, force)
1349 diffopts = self.diffopts()
1353 diffopts = self.diffopts()
1350 with repo.wlock():
1354 with repo.wlock():
1351 heads = []
1355 heads = []
1352 for hs in repo.branchmap().itervalues():
1356 for hs in repo.branchmap().itervalues():
1353 heads.extend(hs)
1357 heads.extend(hs)
1354 if not heads:
1358 if not heads:
1355 heads = [nullid]
1359 heads = [nullid]
1356 if repo.dirstate.p1() not in heads and not exact:
1360 if repo.dirstate.p1() not in heads and not exact:
1357 self.ui.status(_("(working directory not at a head)\n"))
1361 self.ui.status(_("(working directory not at a head)\n"))
1358
1362
1359 if not self.series:
1363 if not self.series:
1360 self.ui.warn(_('no patches in series\n'))
1364 self.ui.warn(_('no patches in series\n'))
1361 return 0
1365 return 0
1362
1366
1363 # Suppose our series file is: A B C and the current 'top'
1367 # Suppose our series file is: A B C and the current 'top'
1364 # patch is B. qpush C should be performed (moving forward)
1368 # patch is B. qpush C should be performed (moving forward)
1365 # qpush B is a NOP (no change) qpush A is an error (can't
1369 # qpush B is a NOP (no change) qpush A is an error (can't
1366 # go backwards with qpush)
1370 # go backwards with qpush)
1367 if patch:
1371 if patch:
1368 patch = self.lookup(patch)
1372 patch = self.lookup(patch)
1369 info = self.isapplied(patch)
1373 info = self.isapplied(patch)
1370 if info and info[0] >= len(self.applied) - 1:
1374 if info and info[0] >= len(self.applied) - 1:
1371 self.ui.warn(
1375 self.ui.warn(
1372 _('qpush: %s is already at the top\n') % patch)
1376 _('qpush: %s is already at the top\n') % patch)
1373 return 0
1377 return 0
1374
1378
1375 pushable, reason = self.pushable(patch)
1379 pushable, reason = self.pushable(patch)
1376 if pushable:
1380 if pushable:
1377 if self.series.index(patch) < self.seriesend():
1381 if self.series.index(patch) < self.seriesend():
1378 raise error.Abort(
1382 raise error.Abort(
1379 _("cannot push to a previous patch: %s") % patch)
1383 _("cannot push to a previous patch: %s") % patch)
1380 else:
1384 else:
1381 if reason:
1385 if reason:
1382 reason = _('guarded by %s') % reason
1386 reason = _('guarded by %s') % reason
1383 else:
1387 else:
1384 reason = _('no matching guards')
1388 reason = _('no matching guards')
1385 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1389 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1386 return 1
1390 return 1
1387 elif all:
1391 elif all:
1388 patch = self.series[-1]
1392 patch = self.series[-1]
1389 if self.isapplied(patch):
1393 if self.isapplied(patch):
1390 self.ui.warn(_('all patches are currently applied\n'))
1394 self.ui.warn(_('all patches are currently applied\n'))
1391 return 0
1395 return 0
1392
1396
1393 # Following the above example, starting at 'top' of B:
1397 # Following the above example, starting at 'top' of B:
1394 # qpush should be performed (pushes C), but a subsequent
1398 # qpush should be performed (pushes C), but a subsequent
1395 # qpush without an argument is an error (nothing to
1399 # qpush without an argument is an error (nothing to
1396 # apply). This allows a loop of "...while hg qpush..." to
1400 # apply). This allows a loop of "...while hg qpush..." to
1397 # work as it detects an error when done
1401 # work as it detects an error when done
1398 start = self.seriesend()
1402 start = self.seriesend()
1399 if start == len(self.series):
1403 if start == len(self.series):
1400 self.ui.warn(_('patch series already fully applied\n'))
1404 self.ui.warn(_('patch series already fully applied\n'))
1401 return 1
1405 return 1
1402 if not force and not keepchanges:
1406 if not force and not keepchanges:
1403 self.checklocalchanges(repo, refresh=self.applied)
1407 self.checklocalchanges(repo, refresh=self.applied)
1404
1408
1405 if exact:
1409 if exact:
1406 if keepchanges:
1410 if keepchanges:
1407 raise error.Abort(
1411 raise error.Abort(
1408 _("cannot use --exact and --keep-changes together"))
1412 _("cannot use --exact and --keep-changes together"))
1409 if move:
1413 if move:
1410 raise error.Abort(_('cannot use --exact and --move '
1414 raise error.Abort(_('cannot use --exact and --move '
1411 'together'))
1415 'together'))
1412 if self.applied:
1416 if self.applied:
1413 raise error.Abort(_('cannot push --exact with applied '
1417 raise error.Abort(_('cannot push --exact with applied '
1414 'patches'))
1418 'patches'))
1415 root = self.series[start]
1419 root = self.series[start]
1416 target = patchheader(self.join(root), self.plainmode).parent
1420 target = patchheader(self.join(root), self.plainmode).parent
1417 if not target:
1421 if not target:
1418 raise error.Abort(
1422 raise error.Abort(
1419 _("%s does not have a parent recorded") % root)
1423 _("%s does not have a parent recorded") % root)
1420 if not repo[target] == repo['.']:
1424 if not repo[target] == repo['.']:
1421 hg.update(repo, target)
1425 hg.update(repo, target)
1422
1426
1423 if move:
1427 if move:
1424 if not patch:
1428 if not patch:
1425 raise error.Abort(_("please specify the patch to move"))
1429 raise error.Abort(_("please specify the patch to move"))
1426 for fullstart, rpn in enumerate(self.fullseries):
1430 for fullstart, rpn in enumerate(self.fullseries):
1427 # strip markers for patch guards
1431 # strip markers for patch guards
1428 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1432 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1429 break
1433 break
1430 for i, rpn in enumerate(self.fullseries[fullstart:]):
1434 for i, rpn in enumerate(self.fullseries[fullstart:]):
1431 # strip markers for patch guards
1435 # strip markers for patch guards
1432 if self.guard_re.split(rpn, 1)[0] == patch:
1436 if self.guard_re.split(rpn, 1)[0] == patch:
1433 break
1437 break
1434 index = fullstart + i
1438 index = fullstart + i
1435 assert index < len(self.fullseries)
1439 assert index < len(self.fullseries)
1436 fullpatch = self.fullseries[index]
1440 fullpatch = self.fullseries[index]
1437 del self.fullseries[index]
1441 del self.fullseries[index]
1438 self.fullseries.insert(fullstart, fullpatch)
1442 self.fullseries.insert(fullstart, fullpatch)
1439 self.parseseries()
1443 self.parseseries()
1440 self.seriesdirty = True
1444 self.seriesdirty = True
1441
1445
1442 self.applieddirty = True
1446 self.applieddirty = True
1443 if start > 0:
1447 if start > 0:
1444 self.checktoppatch(repo)
1448 self.checktoppatch(repo)
1445 if not patch:
1449 if not patch:
1446 patch = self.series[start]
1450 patch = self.series[start]
1447 end = start + 1
1451 end = start + 1
1448 else:
1452 else:
1449 end = self.series.index(patch, start) + 1
1453 end = self.series.index(patch, start) + 1
1450
1454
1451 tobackup = set()
1455 tobackup = set()
1452 if (not nobackup and force) or keepchanges:
1456 if (not nobackup and force) or keepchanges:
1453 status = self.checklocalchanges(repo, force=True)
1457 status = self.checklocalchanges(repo, force=True)
1454 if keepchanges:
1458 if keepchanges:
1455 tobackup.update(status.modified + status.added +
1459 tobackup.update(status.modified + status.added +
1456 status.removed + status.deleted)
1460 status.removed + status.deleted)
1457 else:
1461 else:
1458 tobackup.update(status.modified + status.added)
1462 tobackup.update(status.modified + status.added)
1459
1463
1460 s = self.series[start:end]
1464 s = self.series[start:end]
1461 all_files = set()
1465 all_files = set()
1462 try:
1466 try:
1463 if mergeq:
1467 if mergeq:
1464 ret = self.mergepatch(repo, mergeq, s, diffopts)
1468 ret = self.mergepatch(repo, mergeq, s, diffopts)
1465 else:
1469 else:
1466 ret = self.apply(repo, s, list, all_files=all_files,
1470 ret = self.apply(repo, s, list, all_files=all_files,
1467 tobackup=tobackup, keepchanges=keepchanges)
1471 tobackup=tobackup, keepchanges=keepchanges)
1468 except AbortNoCleanup:
1472 except AbortNoCleanup:
1469 raise
1473 raise
1470 except: # re-raises
1474 except: # re-raises
1471 self.ui.warn(_('cleaning up working directory...\n'))
1475 self.ui.warn(_('cleaning up working directory...\n'))
1472 cmdutil.revert(self.ui, repo, repo['.'],
1476 cmdutil.revert(self.ui, repo, repo['.'],
1473 repo.dirstate.parents(), no_backup=True)
1477 repo.dirstate.parents(), no_backup=True)
1474 # only remove unknown files that we know we touched or
1478 # only remove unknown files that we know we touched or
1475 # created while patching
1479 # created while patching
1476 for f in all_files:
1480 for f in all_files:
1477 if f not in repo.dirstate:
1481 if f not in repo.dirstate:
1478 repo.wvfs.unlinkpath(f, ignoremissing=True)
1482 repo.wvfs.unlinkpath(f, ignoremissing=True)
1479 self.ui.warn(_('done\n'))
1483 self.ui.warn(_('done\n'))
1480 raise
1484 raise
1481
1485
1482 if not self.applied:
1486 if not self.applied:
1483 return ret[0]
1487 return ret[0]
1484 top = self.applied[-1].name
1488 top = self.applied[-1].name
1485 if ret[0] and ret[0] > 1:
1489 if ret[0] and ret[0] > 1:
1486 msg = _("errors during apply, please fix and qrefresh %s\n")
1490 msg = _("errors during apply, please fix and qrefresh %s\n")
1487 self.ui.write(msg % top)
1491 self.ui.write(msg % top)
1488 else:
1492 else:
1489 self.ui.write(_("now at: %s\n") % top)
1493 self.ui.write(_("now at: %s\n") % top)
1490 return ret[0]
1494 return ret[0]
1491
1495
1492 def pop(self, repo, patch=None, force=False, update=True, all=False,
1496 def pop(self, repo, patch=None, force=False, update=True, all=False,
1493 nobackup=False, keepchanges=False):
1497 nobackup=False, keepchanges=False):
1494 self.checkkeepchanges(keepchanges, force)
1498 self.checkkeepchanges(keepchanges, force)
1495 with repo.wlock():
1499 with repo.wlock():
1496 if patch:
1500 if patch:
1497 # index, rev, patch
1501 # index, rev, patch
1498 info = self.isapplied(patch)
1502 info = self.isapplied(patch)
1499 if not info:
1503 if not info:
1500 patch = self.lookup(patch)
1504 patch = self.lookup(patch)
1501 info = self.isapplied(patch)
1505 info = self.isapplied(patch)
1502 if not info:
1506 if not info:
1503 raise error.Abort(_("patch %s is not applied") % patch)
1507 raise error.Abort(_("patch %s is not applied") % patch)
1504
1508
1505 if not self.applied:
1509 if not self.applied:
1506 # Allow qpop -a to work repeatedly,
1510 # Allow qpop -a to work repeatedly,
1507 # but not qpop without an argument
1511 # but not qpop without an argument
1508 self.ui.warn(_("no patches applied\n"))
1512 self.ui.warn(_("no patches applied\n"))
1509 return not all
1513 return not all
1510
1514
1511 if all:
1515 if all:
1512 start = 0
1516 start = 0
1513 elif patch:
1517 elif patch:
1514 start = info[0] + 1
1518 start = info[0] + 1
1515 else:
1519 else:
1516 start = len(self.applied) - 1
1520 start = len(self.applied) - 1
1517
1521
1518 if start >= len(self.applied):
1522 if start >= len(self.applied):
1519 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1523 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1520 return
1524 return
1521
1525
1522 if not update:
1526 if not update:
1523 parents = repo.dirstate.parents()
1527 parents = repo.dirstate.parents()
1524 rr = [x.node for x in self.applied]
1528 rr = [x.node for x in self.applied]
1525 for p in parents:
1529 for p in parents:
1526 if p in rr:
1530 if p in rr:
1527 self.ui.warn(_("qpop: forcing dirstate update\n"))
1531 self.ui.warn(_("qpop: forcing dirstate update\n"))
1528 update = True
1532 update = True
1529 else:
1533 else:
1530 parents = [p.node() for p in repo[None].parents()]
1534 parents = [p.node() for p in repo[None].parents()]
1531 needupdate = False
1535 needupdate = False
1532 for entry in self.applied[start:]:
1536 for entry in self.applied[start:]:
1533 if entry.node in parents:
1537 if entry.node in parents:
1534 needupdate = True
1538 needupdate = True
1535 break
1539 break
1536 update = needupdate
1540 update = needupdate
1537
1541
1538 tobackup = set()
1542 tobackup = set()
1539 if update:
1543 if update:
1540 s = self.checklocalchanges(repo, force=force or keepchanges)
1544 s = self.checklocalchanges(repo, force=force or keepchanges)
1541 if force:
1545 if force:
1542 if not nobackup:
1546 if not nobackup:
1543 tobackup.update(s.modified + s.added)
1547 tobackup.update(s.modified + s.added)
1544 elif keepchanges:
1548 elif keepchanges:
1545 tobackup.update(s.modified + s.added +
1549 tobackup.update(s.modified + s.added +
1546 s.removed + s.deleted)
1550 s.removed + s.deleted)
1547
1551
1548 self.applieddirty = True
1552 self.applieddirty = True
1549 end = len(self.applied)
1553 end = len(self.applied)
1550 rev = self.applied[start].node
1554 rev = self.applied[start].node
1551
1555
1552 try:
1556 try:
1553 heads = repo.changelog.heads(rev)
1557 heads = repo.changelog.heads(rev)
1554 except error.LookupError:
1558 except error.LookupError:
1555 node = short(rev)
1559 node = short(rev)
1556 raise error.Abort(_('trying to pop unknown node %s') % node)
1560 raise error.Abort(_('trying to pop unknown node %s') % node)
1557
1561
1558 if heads != [self.applied[-1].node]:
1562 if heads != [self.applied[-1].node]:
1559 raise error.Abort(_("popping would remove a revision not "
1563 raise error.Abort(_("popping would remove a revision not "
1560 "managed by this patch queue"))
1564 "managed by this patch queue"))
1561 if not repo[self.applied[-1].node].mutable():
1565 if not repo[self.applied[-1].node].mutable():
1562 raise error.Abort(
1566 raise error.Abort(
1563 _("popping would remove a public revision"),
1567 _("popping would remove a public revision"),
1564 hint=_("see 'hg help phases' for details"))
1568 hint=_("see 'hg help phases' for details"))
1565
1569
1566 # we know there are no local changes, so we can make a simplified
1570 # we know there are no local changes, so we can make a simplified
1567 # form of hg.update.
1571 # form of hg.update.
1568 if update:
1572 if update:
1569 qp = self.qparents(repo, rev)
1573 qp = self.qparents(repo, rev)
1570 ctx = repo[qp]
1574 ctx = repo[qp]
1571 m, a, r, d = repo.status(qp, '.')[:4]
1575 m, a, r, d = repo.status(qp, '.')[:4]
1572 if d:
1576 if d:
1573 raise error.Abort(_("deletions found between repo revs"))
1577 raise error.Abort(_("deletions found between repo revs"))
1574
1578
1575 tobackup = set(a + m + r) & tobackup
1579 tobackup = set(a + m + r) & tobackup
1576 if keepchanges and tobackup:
1580 if keepchanges and tobackup:
1577 raise error.Abort(_("local changes found, qrefresh first"))
1581 raise error.Abort(_("local changes found, qrefresh first"))
1578 self.backup(repo, tobackup)
1582 self.backup(repo, tobackup)
1579 repo.dirstate.beginparentchange()
1583 repo.dirstate.beginparentchange()
1580 for f in a:
1584 for f in a:
1581 repo.wvfs.unlinkpath(f, ignoremissing=True)
1585 repo.wvfs.unlinkpath(f, ignoremissing=True)
1582 repo.dirstate.drop(f)
1586 repo.dirstate.drop(f)
1583 for f in m + r:
1587 for f in m + r:
1584 fctx = ctx[f]
1588 fctx = ctx[f]
1585 repo.wwrite(f, fctx.data(), fctx.flags())
1589 repo.wwrite(f, fctx.data(), fctx.flags())
1586 repo.dirstate.normal(f)
1590 repo.dirstate.normal(f)
1587 repo.setparents(qp, nullid)
1591 repo.setparents(qp, nullid)
1588 repo.dirstate.endparentchange()
1592 repo.dirstate.endparentchange()
1589 for patch in reversed(self.applied[start:end]):
1593 for patch in reversed(self.applied[start:end]):
1590 self.ui.status(_("popping %s\n") % patch.name)
1594 self.ui.status(_("popping %s\n") % patch.name)
1591 del self.applied[start:end]
1595 del self.applied[start:end]
1592 strip(self.ui, repo, [rev], update=False, backup=False)
1596 strip(self.ui, repo, [rev], update=False, backup=False)
1593 for s, state in repo['.'].substate.items():
1597 for s, state in repo['.'].substate.items():
1594 repo['.'].sub(s).get(state)
1598 repo['.'].sub(s).get(state)
1595 if self.applied:
1599 if self.applied:
1596 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1600 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1597 else:
1601 else:
1598 self.ui.write(_("patch queue now empty\n"))
1602 self.ui.write(_("patch queue now empty\n"))
1599
1603
1600 def diff(self, repo, pats, opts):
1604 def diff(self, repo, pats, opts):
1601 top, patch = self.checktoppatch(repo)
1605 top, patch = self.checktoppatch(repo)
1602 if not top:
1606 if not top:
1603 self.ui.write(_("no patches applied\n"))
1607 self.ui.write(_("no patches applied\n"))
1604 return
1608 return
1605 qp = self.qparents(repo, top)
1609 qp = self.qparents(repo, top)
1606 if opts.get('reverse'):
1610 if opts.get('reverse'):
1607 node1, node2 = None, qp
1611 node1, node2 = None, qp
1608 else:
1612 else:
1609 node1, node2 = qp, None
1613 node1, node2 = qp, None
1610 diffopts = self.diffopts(opts, patch)
1614 diffopts = self.diffopts(opts, patch)
1611 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1615 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1612
1616
1613 def refresh(self, repo, pats=None, **opts):
1617 def refresh(self, repo, pats=None, **opts):
1614 if not self.applied:
1618 if not self.applied:
1615 self.ui.write(_("no patches applied\n"))
1619 self.ui.write(_("no patches applied\n"))
1616 return 1
1620 return 1
1617 msg = opts.get('msg', '').rstrip()
1621 msg = opts.get('msg', '').rstrip()
1618 edit = opts.get('edit')
1622 edit = opts.get('edit')
1619 editform = opts.get('editform', 'mq.qrefresh')
1623 editform = opts.get('editform', 'mq.qrefresh')
1620 newuser = opts.get('user')
1624 newuser = opts.get('user')
1621 newdate = opts.get('date')
1625 newdate = opts.get('date')
1622 if newdate:
1626 if newdate:
1623 newdate = '%d %d' % util.parsedate(newdate)
1627 newdate = '%d %d' % util.parsedate(newdate)
1624 wlock = repo.wlock()
1628 wlock = repo.wlock()
1625
1629
1626 try:
1630 try:
1627 self.checktoppatch(repo)
1631 self.checktoppatch(repo)
1628 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1632 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1629 if repo.changelog.heads(top) != [top]:
1633 if repo.changelog.heads(top) != [top]:
1630 raise error.Abort(_("cannot qrefresh a revision with children"))
1634 raise error.Abort(_("cannot qrefresh a revision with children"))
1631 if not repo[top].mutable():
1635 if not repo[top].mutable():
1632 raise error.Abort(_("cannot qrefresh public revision"),
1636 raise error.Abort(_("cannot qrefresh public revision"),
1633 hint=_("see 'hg help phases' for details"))
1637 hint=_("see 'hg help phases' for details"))
1634
1638
1635 cparents = repo.changelog.parents(top)
1639 cparents = repo.changelog.parents(top)
1636 patchparent = self.qparents(repo, top)
1640 patchparent = self.qparents(repo, top)
1637
1641
1638 inclsubs = checksubstate(repo, hex(patchparent))
1642 inclsubs = checksubstate(repo, hex(patchparent))
1639 if inclsubs:
1643 if inclsubs:
1640 substatestate = repo.dirstate['.hgsubstate']
1644 substatestate = repo.dirstate['.hgsubstate']
1641
1645
1642 ph = patchheader(self.join(patchfn), self.plainmode)
1646 ph = patchheader(self.join(patchfn), self.plainmode)
1643 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1647 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1644 if newuser:
1648 if newuser:
1645 ph.setuser(newuser)
1649 ph.setuser(newuser)
1646 if newdate:
1650 if newdate:
1647 ph.setdate(newdate)
1651 ph.setdate(newdate)
1648 ph.setparent(hex(patchparent))
1652 ph.setparent(hex(patchparent))
1649
1653
1650 # only commit new patch when write is complete
1654 # only commit new patch when write is complete
1651 patchf = self.opener(patchfn, 'w', atomictemp=True)
1655 patchf = self.opener(patchfn, 'w', atomictemp=True)
1652
1656
1653 # update the dirstate in place, strip off the qtip commit
1657 # update the dirstate in place, strip off the qtip commit
1654 # and then commit.
1658 # and then commit.
1655 #
1659 #
1656 # this should really read:
1660 # this should really read:
1657 # mm, dd, aa = repo.status(top, patchparent)[:3]
1661 # mm, dd, aa = repo.status(top, patchparent)[:3]
1658 # but we do it backwards to take advantage of manifest/changelog
1662 # but we do it backwards to take advantage of manifest/changelog
1659 # caching against the next repo.status call
1663 # caching against the next repo.status call
1660 mm, aa, dd = repo.status(patchparent, top)[:3]
1664 mm, aa, dd = repo.status(patchparent, top)[:3]
1661 changes = repo.changelog.read(top)
1665 changes = repo.changelog.read(top)
1662 man = repo.manifestlog[changes[0]].read()
1666 man = repo.manifestlog[changes[0]].read()
1663 aaa = aa[:]
1667 aaa = aa[:]
1664 matchfn = scmutil.match(repo[None], pats, opts)
1668 matchfn = scmutil.match(repo[None], pats, opts)
1665 # in short mode, we only diff the files included in the
1669 # in short mode, we only diff the files included in the
1666 # patch already plus specified files
1670 # patch already plus specified files
1667 if opts.get('short'):
1671 if opts.get('short'):
1668 # if amending a patch, we start with existing
1672 # if amending a patch, we start with existing
1669 # files plus specified files - unfiltered
1673 # files plus specified files - unfiltered
1670 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1674 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1671 # filter with include/exclude options
1675 # filter with include/exclude options
1672 matchfn = scmutil.match(repo[None], opts=opts)
1676 matchfn = scmutil.match(repo[None], opts=opts)
1673 else:
1677 else:
1674 match = scmutil.matchall(repo)
1678 match = scmutil.matchall(repo)
1675 m, a, r, d = repo.status(match=match)[:4]
1679 m, a, r, d = repo.status(match=match)[:4]
1676 mm = set(mm)
1680 mm = set(mm)
1677 aa = set(aa)
1681 aa = set(aa)
1678 dd = set(dd)
1682 dd = set(dd)
1679
1683
1680 # we might end up with files that were added between
1684 # we might end up with files that were added between
1681 # qtip and the dirstate parent, but then changed in the
1685 # qtip and the dirstate parent, but then changed in the
1682 # local dirstate. in this case, we want them to only
1686 # local dirstate. in this case, we want them to only
1683 # show up in the added section
1687 # show up in the added section
1684 for x in m:
1688 for x in m:
1685 if x not in aa:
1689 if x not in aa:
1686 mm.add(x)
1690 mm.add(x)
1687 # we might end up with files added by the local dirstate that
1691 # we might end up with files added by the local dirstate that
1688 # were deleted by the patch. In this case, they should only
1692 # were deleted by the patch. In this case, they should only
1689 # show up in the changed section.
1693 # show up in the changed section.
1690 for x in a:
1694 for x in a:
1691 if x in dd:
1695 if x in dd:
1692 dd.remove(x)
1696 dd.remove(x)
1693 mm.add(x)
1697 mm.add(x)
1694 else:
1698 else:
1695 aa.add(x)
1699 aa.add(x)
1696 # make sure any files deleted in the local dirstate
1700 # make sure any files deleted in the local dirstate
1697 # are not in the add or change column of the patch
1701 # are not in the add or change column of the patch
1698 forget = []
1702 forget = []
1699 for x in d + r:
1703 for x in d + r:
1700 if x in aa:
1704 if x in aa:
1701 aa.remove(x)
1705 aa.remove(x)
1702 forget.append(x)
1706 forget.append(x)
1703 continue
1707 continue
1704 else:
1708 else:
1705 mm.discard(x)
1709 mm.discard(x)
1706 dd.add(x)
1710 dd.add(x)
1707
1711
1708 m = list(mm)
1712 m = list(mm)
1709 r = list(dd)
1713 r = list(dd)
1710 a = list(aa)
1714 a = list(aa)
1711
1715
1712 # create 'match' that includes the files to be recommitted.
1716 # create 'match' that includes the files to be recommitted.
1713 # apply matchfn via repo.status to ensure correct case handling.
1717 # apply matchfn via repo.status to ensure correct case handling.
1714 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1718 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1715 allmatches = set(cm + ca + cr + cd)
1719 allmatches = set(cm + ca + cr + cd)
1716 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1720 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1717
1721
1718 files = set(inclsubs)
1722 files = set(inclsubs)
1719 for x in refreshchanges:
1723 for x in refreshchanges:
1720 files.update(x)
1724 files.update(x)
1721 match = scmutil.matchfiles(repo, files)
1725 match = scmutil.matchfiles(repo, files)
1722
1726
1723 bmlist = repo[top].bookmarks()
1727 bmlist = repo[top].bookmarks()
1724
1728
1725 dsguard = None
1729 dsguard = None
1726 try:
1730 try:
1727 dsguard = dirstateguard.dirstateguard(repo, 'mq.refresh')
1731 dsguard = dirstateguard.dirstateguard(repo, 'mq.refresh')
1728 if diffopts.git or diffopts.upgrade:
1732 if diffopts.git or diffopts.upgrade:
1729 copies = {}
1733 copies = {}
1730 for dst in a:
1734 for dst in a:
1731 src = repo.dirstate.copied(dst)
1735 src = repo.dirstate.copied(dst)
1732 # during qfold, the source file for copies may
1736 # during qfold, the source file for copies may
1733 # be removed. Treat this as a simple add.
1737 # be removed. Treat this as a simple add.
1734 if src is not None and src in repo.dirstate:
1738 if src is not None and src in repo.dirstate:
1735 copies.setdefault(src, []).append(dst)
1739 copies.setdefault(src, []).append(dst)
1736 repo.dirstate.add(dst)
1740 repo.dirstate.add(dst)
1737 # remember the copies between patchparent and qtip
1741 # remember the copies between patchparent and qtip
1738 for dst in aaa:
1742 for dst in aaa:
1739 f = repo.file(dst)
1743 f = repo.file(dst)
1740 src = f.renamed(man[dst])
1744 src = f.renamed(man[dst])
1741 if src:
1745 if src:
1742 copies.setdefault(src[0], []).extend(
1746 copies.setdefault(src[0], []).extend(
1743 copies.get(dst, []))
1747 copies.get(dst, []))
1744 if dst in a:
1748 if dst in a:
1745 copies[src[0]].append(dst)
1749 copies[src[0]].append(dst)
1746 # we can't copy a file created by the patch itself
1750 # we can't copy a file created by the patch itself
1747 if dst in copies:
1751 if dst in copies:
1748 del copies[dst]
1752 del copies[dst]
1749 for src, dsts in copies.iteritems():
1753 for src, dsts in copies.iteritems():
1750 for dst in dsts:
1754 for dst in dsts:
1751 repo.dirstate.copy(src, dst)
1755 repo.dirstate.copy(src, dst)
1752 else:
1756 else:
1753 for dst in a:
1757 for dst in a:
1754 repo.dirstate.add(dst)
1758 repo.dirstate.add(dst)
1755 # Drop useless copy information
1759 # Drop useless copy information
1756 for f in list(repo.dirstate.copies()):
1760 for f in list(repo.dirstate.copies()):
1757 repo.dirstate.copy(None, f)
1761 repo.dirstate.copy(None, f)
1758 for f in r:
1762 for f in r:
1759 repo.dirstate.remove(f)
1763 repo.dirstate.remove(f)
1760 # if the patch excludes a modified file, mark that
1764 # if the patch excludes a modified file, mark that
1761 # file with mtime=0 so status can see it.
1765 # file with mtime=0 so status can see it.
1762 mm = []
1766 mm = []
1763 for i in xrange(len(m) - 1, -1, -1):
1767 for i in xrange(len(m) - 1, -1, -1):
1764 if not matchfn(m[i]):
1768 if not matchfn(m[i]):
1765 mm.append(m[i])
1769 mm.append(m[i])
1766 del m[i]
1770 del m[i]
1767 for f in m:
1771 for f in m:
1768 repo.dirstate.normal(f)
1772 repo.dirstate.normal(f)
1769 for f in mm:
1773 for f in mm:
1770 repo.dirstate.normallookup(f)
1774 repo.dirstate.normallookup(f)
1771 for f in forget:
1775 for f in forget:
1772 repo.dirstate.drop(f)
1776 repo.dirstate.drop(f)
1773
1777
1774 user = ph.user or changes[1]
1778 user = ph.user or changes[1]
1775
1779
1776 oldphase = repo[top].phase()
1780 oldphase = repo[top].phase()
1777
1781
1778 # assumes strip can roll itself back if interrupted
1782 # assumes strip can roll itself back if interrupted
1779 repo.setparents(*cparents)
1783 repo.setparents(*cparents)
1780 self.applied.pop()
1784 self.applied.pop()
1781 self.applieddirty = True
1785 self.applieddirty = True
1782 strip(self.ui, repo, [top], update=False, backup=False)
1786 strip(self.ui, repo, [top], update=False, backup=False)
1783 dsguard.close()
1787 dsguard.close()
1784 finally:
1788 finally:
1785 release(dsguard)
1789 release(dsguard)
1786
1790
1787 try:
1791 try:
1788 # might be nice to attempt to roll back strip after this
1792 # might be nice to attempt to roll back strip after this
1789
1793
1790 defaultmsg = "[mq]: %s" % patchfn
1794 defaultmsg = "[mq]: %s" % patchfn
1791 editor = cmdutil.getcommiteditor(editform=editform)
1795 editor = cmdutil.getcommiteditor(editform=editform)
1792 if edit:
1796 if edit:
1793 def finishdesc(desc):
1797 def finishdesc(desc):
1794 if desc.rstrip():
1798 if desc.rstrip():
1795 ph.setmessage(desc)
1799 ph.setmessage(desc)
1796 return desc
1800 return desc
1797 return defaultmsg
1801 return defaultmsg
1798 # i18n: this message is shown in editor with "HG: " prefix
1802 # i18n: this message is shown in editor with "HG: " prefix
1799 extramsg = _('Leave message empty to use default message.')
1803 extramsg = _('Leave message empty to use default message.')
1800 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1804 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1801 extramsg=extramsg,
1805 extramsg=extramsg,
1802 editform=editform)
1806 editform=editform)
1803 message = msg or "\n".join(ph.message)
1807 message = msg or "\n".join(ph.message)
1804 elif not msg:
1808 elif not msg:
1805 if not ph.message:
1809 if not ph.message:
1806 message = defaultmsg
1810 message = defaultmsg
1807 else:
1811 else:
1808 message = "\n".join(ph.message)
1812 message = "\n".join(ph.message)
1809 else:
1813 else:
1810 message = msg
1814 message = msg
1811 ph.setmessage(msg)
1815 ph.setmessage(msg)
1812
1816
1813 # Ensure we create a new changeset in the same phase than
1817 # Ensure we create a new changeset in the same phase than
1814 # the old one.
1818 # the old one.
1815 lock = tr = None
1819 lock = tr = None
1816 try:
1820 try:
1817 lock = repo.lock()
1821 lock = repo.lock()
1818 tr = repo.transaction('mq')
1822 tr = repo.transaction('mq')
1819 n = newcommit(repo, oldphase, message, user, ph.date,
1823 n = newcommit(repo, oldphase, message, user, ph.date,
1820 match=match, force=True, editor=editor)
1824 match=match, force=True, editor=editor)
1821 # only write patch after a successful commit
1825 # only write patch after a successful commit
1822 c = [list(x) for x in refreshchanges]
1826 c = [list(x) for x in refreshchanges]
1823 if inclsubs:
1827 if inclsubs:
1824 self.putsubstate2changes(substatestate, c)
1828 self.putsubstate2changes(substatestate, c)
1825 chunks = patchmod.diff(repo, patchparent,
1829 chunks = patchmod.diff(repo, patchparent,
1826 changes=c, opts=diffopts)
1830 changes=c, opts=diffopts)
1827 comments = str(ph)
1831 comments = str(ph)
1828 if comments:
1832 if comments:
1829 patchf.write(comments)
1833 patchf.write(comments)
1830 for chunk in chunks:
1834 for chunk in chunks:
1831 patchf.write(chunk)
1835 patchf.write(chunk)
1832 patchf.close()
1836 patchf.close()
1833
1837
1834 marks = repo._bookmarks
1838 marks = repo._bookmarks
1835 for bm in bmlist:
1839 for bm in bmlist:
1836 marks[bm] = n
1840 marks[bm] = n
1837 marks.recordchange(tr)
1841 marks.recordchange(tr)
1838 tr.close()
1842 tr.close()
1839
1843
1840 self.applied.append(statusentry(n, patchfn))
1844 self.applied.append(statusentry(n, patchfn))
1841 finally:
1845 finally:
1842 lockmod.release(tr, lock)
1846 lockmod.release(tr, lock)
1843 except: # re-raises
1847 except: # re-raises
1844 ctx = repo[cparents[0]]
1848 ctx = repo[cparents[0]]
1845 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1849 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1846 self.savedirty()
1850 self.savedirty()
1847 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1851 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1848 '(revert --all, qpush to recover)\n'))
1852 '(revert --all, qpush to recover)\n'))
1849 raise
1853 raise
1850 finally:
1854 finally:
1851 wlock.release()
1855 wlock.release()
1852 self.removeundo(repo)
1856 self.removeundo(repo)
1853
1857
1854 def init(self, repo, create=False):
1858 def init(self, repo, create=False):
1855 if not create and os.path.isdir(self.path):
1859 if not create and os.path.isdir(self.path):
1856 raise error.Abort(_("patch queue directory already exists"))
1860 raise error.Abort(_("patch queue directory already exists"))
1857 try:
1861 try:
1858 os.mkdir(self.path)
1862 os.mkdir(self.path)
1859 except OSError as inst:
1863 except OSError as inst:
1860 if inst.errno != errno.EEXIST or not create:
1864 if inst.errno != errno.EEXIST or not create:
1861 raise
1865 raise
1862 if create:
1866 if create:
1863 return self.qrepo(create=True)
1867 return self.qrepo(create=True)
1864
1868
1865 def unapplied(self, repo, patch=None):
1869 def unapplied(self, repo, patch=None):
1866 if patch and patch not in self.series:
1870 if patch and patch not in self.series:
1867 raise error.Abort(_("patch %s is not in series file") % patch)
1871 raise error.Abort(_("patch %s is not in series file") % patch)
1868 if not patch:
1872 if not patch:
1869 start = self.seriesend()
1873 start = self.seriesend()
1870 else:
1874 else:
1871 start = self.series.index(patch) + 1
1875 start = self.series.index(patch) + 1
1872 unapplied = []
1876 unapplied = []
1873 for i in xrange(start, len(self.series)):
1877 for i in xrange(start, len(self.series)):
1874 pushable, reason = self.pushable(i)
1878 pushable, reason = self.pushable(i)
1875 if pushable:
1879 if pushable:
1876 unapplied.append((i, self.series[i]))
1880 unapplied.append((i, self.series[i]))
1877 self.explainpushable(i)
1881 self.explainpushable(i)
1878 return unapplied
1882 return unapplied
1879
1883
1880 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1884 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1881 summary=False):
1885 summary=False):
1882 def displayname(pfx, patchname, state):
1886 def displayname(pfx, patchname, state):
1883 if pfx:
1887 if pfx:
1884 self.ui.write(pfx)
1888 self.ui.write(pfx)
1885 if summary:
1889 if summary:
1886 ph = patchheader(self.join(patchname), self.plainmode)
1890 ph = patchheader(self.join(patchname), self.plainmode)
1887 if ph.message:
1891 if ph.message:
1888 msg = ph.message[0]
1892 msg = ph.message[0]
1889 else:
1893 else:
1890 msg = ''
1894 msg = ''
1891
1895
1892 if self.ui.formatted():
1896 if self.ui.formatted():
1893 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1897 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1894 if width > 0:
1898 if width > 0:
1895 msg = util.ellipsis(msg, width)
1899 msg = util.ellipsis(msg, width)
1896 else:
1900 else:
1897 msg = ''
1901 msg = ''
1898 self.ui.write(patchname, label='qseries.' + state)
1902 self.ui.write(patchname, label='qseries.' + state)
1899 self.ui.write(': ')
1903 self.ui.write(': ')
1900 self.ui.write(msg, label='qseries.message.' + state)
1904 self.ui.write(msg, label='qseries.message.' + state)
1901 else:
1905 else:
1902 self.ui.write(patchname, label='qseries.' + state)
1906 self.ui.write(patchname, label='qseries.' + state)
1903 self.ui.write('\n')
1907 self.ui.write('\n')
1904
1908
1905 applied = set([p.name for p in self.applied])
1909 applied = set([p.name for p in self.applied])
1906 if length is None:
1910 if length is None:
1907 length = len(self.series) - start
1911 length = len(self.series) - start
1908 if not missing:
1912 if not missing:
1909 if self.ui.verbose:
1913 if self.ui.verbose:
1910 idxwidth = len(str(start + length - 1))
1914 idxwidth = len(str(start + length - 1))
1911 for i in xrange(start, start + length):
1915 for i in xrange(start, start + length):
1912 patch = self.series[i]
1916 patch = self.series[i]
1913 if patch in applied:
1917 if patch in applied:
1914 char, state = 'A', 'applied'
1918 char, state = 'A', 'applied'
1915 elif self.pushable(i)[0]:
1919 elif self.pushable(i)[0]:
1916 char, state = 'U', 'unapplied'
1920 char, state = 'U', 'unapplied'
1917 else:
1921 else:
1918 char, state = 'G', 'guarded'
1922 char, state = 'G', 'guarded'
1919 pfx = ''
1923 pfx = ''
1920 if self.ui.verbose:
1924 if self.ui.verbose:
1921 pfx = '%*d %s ' % (idxwidth, i, char)
1925 pfx = '%*d %s ' % (idxwidth, i, char)
1922 elif status and status != char:
1926 elif status and status != char:
1923 continue
1927 continue
1924 displayname(pfx, patch, state)
1928 displayname(pfx, patch, state)
1925 else:
1929 else:
1926 msng_list = []
1930 msng_list = []
1927 for root, dirs, files in os.walk(self.path):
1931 for root, dirs, files in os.walk(self.path):
1928 d = root[len(self.path) + 1:]
1932 d = root[len(self.path) + 1:]
1929 for f in files:
1933 for f in files:
1930 fl = os.path.join(d, f)
1934 fl = os.path.join(d, f)
1931 if (fl not in self.series and
1935 if (fl not in self.series and
1932 fl not in (self.statuspath, self.seriespath,
1936 fl not in (self.statuspath, self.seriespath,
1933 self.guardspath)
1937 self.guardspath)
1934 and not fl.startswith('.')):
1938 and not fl.startswith('.')):
1935 msng_list.append(fl)
1939 msng_list.append(fl)
1936 for x in sorted(msng_list):
1940 for x in sorted(msng_list):
1937 pfx = self.ui.verbose and ('D ') or ''
1941 pfx = self.ui.verbose and ('D ') or ''
1938 displayname(pfx, x, 'missing')
1942 displayname(pfx, x, 'missing')
1939
1943
1940 def issaveline(self, l):
1944 def issaveline(self, l):
1941 if l.name == '.hg.patches.save.line':
1945 if l.name == '.hg.patches.save.line':
1942 return True
1946 return True
1943
1947
1944 def qrepo(self, create=False):
1948 def qrepo(self, create=False):
1945 ui = self.baseui.copy()
1949 ui = self.baseui.copy()
1946 if create or os.path.isdir(self.join(".hg")):
1950 if create or os.path.isdir(self.join(".hg")):
1947 return hg.repository(ui, path=self.path, create=create)
1951 return hg.repository(ui, path=self.path, create=create)
1948
1952
1949 def restore(self, repo, rev, delete=None, qupdate=None):
1953 def restore(self, repo, rev, delete=None, qupdate=None):
1950 desc = repo[rev].description().strip()
1954 desc = repo[rev].description().strip()
1951 lines = desc.splitlines()
1955 lines = desc.splitlines()
1952 i = 0
1956 i = 0
1953 datastart = None
1957 datastart = None
1954 series = []
1958 series = []
1955 applied = []
1959 applied = []
1956 qpp = None
1960 qpp = None
1957 for i, line in enumerate(lines):
1961 for i, line in enumerate(lines):
1958 if line == 'Patch Data:':
1962 if line == 'Patch Data:':
1959 datastart = i + 1
1963 datastart = i + 1
1960 elif line.startswith('Dirstate:'):
1964 elif line.startswith('Dirstate:'):
1961 l = line.rstrip()
1965 l = line.rstrip()
1962 l = l[10:].split(' ')
1966 l = l[10:].split(' ')
1963 qpp = [bin(x) for x in l]
1967 qpp = [bin(x) for x in l]
1964 elif datastart is not None:
1968 elif datastart is not None:
1965 l = line.rstrip()
1969 l = line.rstrip()
1966 n, name = l.split(':', 1)
1970 n, name = l.split(':', 1)
1967 if n:
1971 if n:
1968 applied.append(statusentry(bin(n), name))
1972 applied.append(statusentry(bin(n), name))
1969 else:
1973 else:
1970 series.append(l)
1974 series.append(l)
1971 if datastart is None:
1975 if datastart is None:
1972 self.ui.warn(_("no saved patch data found\n"))
1976 self.ui.warn(_("no saved patch data found\n"))
1973 return 1
1977 return 1
1974 self.ui.warn(_("restoring status: %s\n") % lines[0])
1978 self.ui.warn(_("restoring status: %s\n") % lines[0])
1975 self.fullseries = series
1979 self.fullseries = series
1976 self.applied = applied
1980 self.applied = applied
1977 self.parseseries()
1981 self.parseseries()
1978 self.seriesdirty = True
1982 self.seriesdirty = True
1979 self.applieddirty = True
1983 self.applieddirty = True
1980 heads = repo.changelog.heads()
1984 heads = repo.changelog.heads()
1981 if delete:
1985 if delete:
1982 if rev not in heads:
1986 if rev not in heads:
1983 self.ui.warn(_("save entry has children, leaving it alone\n"))
1987 self.ui.warn(_("save entry has children, leaving it alone\n"))
1984 else:
1988 else:
1985 self.ui.warn(_("removing save entry %s\n") % short(rev))
1989 self.ui.warn(_("removing save entry %s\n") % short(rev))
1986 pp = repo.dirstate.parents()
1990 pp = repo.dirstate.parents()
1987 if rev in pp:
1991 if rev in pp:
1988 update = True
1992 update = True
1989 else:
1993 else:
1990 update = False
1994 update = False
1991 strip(self.ui, repo, [rev], update=update, backup=False)
1995 strip(self.ui, repo, [rev], update=update, backup=False)
1992 if qpp:
1996 if qpp:
1993 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1997 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1994 (short(qpp[0]), short(qpp[1])))
1998 (short(qpp[0]), short(qpp[1])))
1995 if qupdate:
1999 if qupdate:
1996 self.ui.status(_("updating queue directory\n"))
2000 self.ui.status(_("updating queue directory\n"))
1997 r = self.qrepo()
2001 r = self.qrepo()
1998 if not r:
2002 if not r:
1999 self.ui.warn(_("unable to load queue repository\n"))
2003 self.ui.warn(_("unable to load queue repository\n"))
2000 return 1
2004 return 1
2001 hg.clean(r, qpp[0])
2005 hg.clean(r, qpp[0])
2002
2006
2003 def save(self, repo, msg=None):
2007 def save(self, repo, msg=None):
2004 if not self.applied:
2008 if not self.applied:
2005 self.ui.warn(_("save: no patches applied, exiting\n"))
2009 self.ui.warn(_("save: no patches applied, exiting\n"))
2006 return 1
2010 return 1
2007 if self.issaveline(self.applied[-1]):
2011 if self.issaveline(self.applied[-1]):
2008 self.ui.warn(_("status is already saved\n"))
2012 self.ui.warn(_("status is already saved\n"))
2009 return 1
2013 return 1
2010
2014
2011 if not msg:
2015 if not msg:
2012 msg = _("hg patches saved state")
2016 msg = _("hg patches saved state")
2013 else:
2017 else:
2014 msg = "hg patches: " + msg.rstrip('\r\n')
2018 msg = "hg patches: " + msg.rstrip('\r\n')
2015 r = self.qrepo()
2019 r = self.qrepo()
2016 if r:
2020 if r:
2017 pp = r.dirstate.parents()
2021 pp = r.dirstate.parents()
2018 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2022 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2019 msg += "\n\nPatch Data:\n"
2023 msg += "\n\nPatch Data:\n"
2020 msg += ''.join('%s\n' % x for x in self.applied)
2024 msg += ''.join('%s\n' % x for x in self.applied)
2021 msg += ''.join(':%s\n' % x for x in self.fullseries)
2025 msg += ''.join(':%s\n' % x for x in self.fullseries)
2022 n = repo.commit(msg, force=True)
2026 n = repo.commit(msg, force=True)
2023 if not n:
2027 if not n:
2024 self.ui.warn(_("repo commit failed\n"))
2028 self.ui.warn(_("repo commit failed\n"))
2025 return 1
2029 return 1
2026 self.applied.append(statusentry(n, '.hg.patches.save.line'))
2030 self.applied.append(statusentry(n, '.hg.patches.save.line'))
2027 self.applieddirty = True
2031 self.applieddirty = True
2028 self.removeundo(repo)
2032 self.removeundo(repo)
2029
2033
2030 def fullseriesend(self):
2034 def fullseriesend(self):
2031 if self.applied:
2035 if self.applied:
2032 p = self.applied[-1].name
2036 p = self.applied[-1].name
2033 end = self.findseries(p)
2037 end = self.findseries(p)
2034 if end is None:
2038 if end is None:
2035 return len(self.fullseries)
2039 return len(self.fullseries)
2036 return end + 1
2040 return end + 1
2037 return 0
2041 return 0
2038
2042
2039 def seriesend(self, all_patches=False):
2043 def seriesend(self, all_patches=False):
2040 """If all_patches is False, return the index of the next pushable patch
2044 """If all_patches is False, return the index of the next pushable patch
2041 in the series, or the series length. If all_patches is True, return the
2045 in the series, or the series length. If all_patches is True, return the
2042 index of the first patch past the last applied one.
2046 index of the first patch past the last applied one.
2043 """
2047 """
2044 end = 0
2048 end = 0
2045 def nextpatch(start):
2049 def nextpatch(start):
2046 if all_patches or start >= len(self.series):
2050 if all_patches or start >= len(self.series):
2047 return start
2051 return start
2048 for i in xrange(start, len(self.series)):
2052 for i in xrange(start, len(self.series)):
2049 p, reason = self.pushable(i)
2053 p, reason = self.pushable(i)
2050 if p:
2054 if p:
2051 return i
2055 return i
2052 self.explainpushable(i)
2056 self.explainpushable(i)
2053 return len(self.series)
2057 return len(self.series)
2054 if self.applied:
2058 if self.applied:
2055 p = self.applied[-1].name
2059 p = self.applied[-1].name
2056 try:
2060 try:
2057 end = self.series.index(p)
2061 end = self.series.index(p)
2058 except ValueError:
2062 except ValueError:
2059 return 0
2063 return 0
2060 return nextpatch(end + 1)
2064 return nextpatch(end + 1)
2061 return nextpatch(end)
2065 return nextpatch(end)
2062
2066
2063 def appliedname(self, index):
2067 def appliedname(self, index):
2064 pname = self.applied[index].name
2068 pname = self.applied[index].name
2065 if not self.ui.verbose:
2069 if not self.ui.verbose:
2066 p = pname
2070 p = pname
2067 else:
2071 else:
2068 p = str(self.series.index(pname)) + " " + pname
2072 p = str(self.series.index(pname)) + " " + pname
2069 return p
2073 return p
2070
2074
2071 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2075 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2072 force=None, git=False):
2076 force=None, git=False):
2073 def checkseries(patchname):
2077 def checkseries(patchname):
2074 if patchname in self.series:
2078 if patchname in self.series:
2075 raise error.Abort(_('patch %s is already in the series file')
2079 raise error.Abort(_('patch %s is already in the series file')
2076 % patchname)
2080 % patchname)
2077
2081
2078 if rev:
2082 if rev:
2079 if files:
2083 if files:
2080 raise error.Abort(_('option "-r" not valid when importing '
2084 raise error.Abort(_('option "-r" not valid when importing '
2081 'files'))
2085 'files'))
2082 rev = scmutil.revrange(repo, rev)
2086 rev = scmutil.revrange(repo, rev)
2083 rev.sort(reverse=True)
2087 rev.sort(reverse=True)
2084 elif not files:
2088 elif not files:
2085 raise error.Abort(_('no files or revisions specified'))
2089 raise error.Abort(_('no files or revisions specified'))
2086 if (len(files) > 1 or len(rev) > 1) and patchname:
2090 if (len(files) > 1 or len(rev) > 1) and patchname:
2087 raise error.Abort(_('option "-n" not valid when importing multiple '
2091 raise error.Abort(_('option "-n" not valid when importing multiple '
2088 'patches'))
2092 'patches'))
2089 imported = []
2093 imported = []
2090 if rev:
2094 if rev:
2091 # If mq patches are applied, we can only import revisions
2095 # If mq patches are applied, we can only import revisions
2092 # that form a linear path to qbase.
2096 # that form a linear path to qbase.
2093 # Otherwise, they should form a linear path to a head.
2097 # Otherwise, they should form a linear path to a head.
2094 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2098 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2095 if len(heads) > 1:
2099 if len(heads) > 1:
2096 raise error.Abort(_('revision %d is the root of more than one '
2100 raise error.Abort(_('revision %d is the root of more than one '
2097 'branch') % rev.last())
2101 'branch') % rev.last())
2098 if self.applied:
2102 if self.applied:
2099 base = repo.changelog.node(rev.first())
2103 base = repo.changelog.node(rev.first())
2100 if base in [n.node for n in self.applied]:
2104 if base in [n.node for n in self.applied]:
2101 raise error.Abort(_('revision %d is already managed')
2105 raise error.Abort(_('revision %d is already managed')
2102 % rev.first())
2106 % rev.first())
2103 if heads != [self.applied[-1].node]:
2107 if heads != [self.applied[-1].node]:
2104 raise error.Abort(_('revision %d is not the parent of '
2108 raise error.Abort(_('revision %d is not the parent of '
2105 'the queue') % rev.first())
2109 'the queue') % rev.first())
2106 base = repo.changelog.rev(self.applied[0].node)
2110 base = repo.changelog.rev(self.applied[0].node)
2107 lastparent = repo.changelog.parentrevs(base)[0]
2111 lastparent = repo.changelog.parentrevs(base)[0]
2108 else:
2112 else:
2109 if heads != [repo.changelog.node(rev.first())]:
2113 if heads != [repo.changelog.node(rev.first())]:
2110 raise error.Abort(_('revision %d has unmanaged children')
2114 raise error.Abort(_('revision %d has unmanaged children')
2111 % rev.first())
2115 % rev.first())
2112 lastparent = None
2116 lastparent = None
2113
2117
2114 diffopts = self.diffopts({'git': git})
2118 diffopts = self.diffopts({'git': git})
2115 with repo.transaction('qimport') as tr:
2119 with repo.transaction('qimport') as tr:
2116 for r in rev:
2120 for r in rev:
2117 if not repo[r].mutable():
2121 if not repo[r].mutable():
2118 raise error.Abort(_('revision %d is not mutable') % r,
2122 raise error.Abort(_('revision %d is not mutable') % r,
2119 hint=_("see 'hg help phases' "
2123 hint=_("see 'hg help phases' "
2120 'for details'))
2124 'for details'))
2121 p1, p2 = repo.changelog.parentrevs(r)
2125 p1, p2 = repo.changelog.parentrevs(r)
2122 n = repo.changelog.node(r)
2126 n = repo.changelog.node(r)
2123 if p2 != nullrev:
2127 if p2 != nullrev:
2124 raise error.Abort(_('cannot import merge revision %d')
2128 raise error.Abort(_('cannot import merge revision %d')
2125 % r)
2129 % r)
2126 if lastparent and lastparent != r:
2130 if lastparent and lastparent != r:
2127 raise error.Abort(_('revision %d is not the parent of '
2131 raise error.Abort(_('revision %d is not the parent of '
2128 '%d')
2132 '%d')
2129 % (r, lastparent))
2133 % (r, lastparent))
2130 lastparent = p1
2134 lastparent = p1
2131
2135
2132 if not patchname:
2136 if not patchname:
2133 patchname = self.makepatchname(
2137 patchname = self.makepatchname(
2134 repo[r].description().split('\n', 1)[0],
2138 repo[r].description().split('\n', 1)[0],
2135 '%d.diff' % r)
2139 '%d.diff' % r)
2136 checkseries(patchname)
2140 checkseries(patchname)
2137 self.checkpatchname(patchname, force)
2141 self.checkpatchname(patchname, force)
2138 self.fullseries.insert(0, patchname)
2142 self.fullseries.insert(0, patchname)
2139
2143
2140 patchf = self.opener(patchname, "w")
2144 patchf = self.opener(patchname, "w")
2141 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2145 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2142 patchf.close()
2146 patchf.close()
2143
2147
2144 se = statusentry(n, patchname)
2148 se = statusentry(n, patchname)
2145 self.applied.insert(0, se)
2149 self.applied.insert(0, se)
2146
2150
2147 self.added.append(patchname)
2151 self.added.append(patchname)
2148 imported.append(patchname)
2152 imported.append(patchname)
2149 patchname = None
2153 patchname = None
2150 if rev and repo.ui.configbool('mq', 'secret', False):
2154 if rev and repo.ui.configbool('mq', 'secret', False):
2151 # if we added anything with --rev, move the secret root
2155 # if we added anything with --rev, move the secret root
2152 phases.retractboundary(repo, tr, phases.secret, [n])
2156 phases.retractboundary(repo, tr, phases.secret, [n])
2153 self.parseseries()
2157 self.parseseries()
2154 self.applieddirty = True
2158 self.applieddirty = True
2155 self.seriesdirty = True
2159 self.seriesdirty = True
2156
2160
2157 for i, filename in enumerate(files):
2161 for i, filename in enumerate(files):
2158 if existing:
2162 if existing:
2159 if filename == '-':
2163 if filename == '-':
2160 raise error.Abort(_('-e is incompatible with import from -')
2164 raise error.Abort(_('-e is incompatible with import from -')
2161 )
2165 )
2162 filename = normname(filename)
2166 filename = normname(filename)
2163 self.checkreservedname(filename)
2167 self.checkreservedname(filename)
2164 if util.url(filename).islocal():
2168 if util.url(filename).islocal():
2165 originpath = self.join(filename)
2169 originpath = self.join(filename)
2166 if not os.path.isfile(originpath):
2170 if not os.path.isfile(originpath):
2167 raise error.Abort(
2171 raise error.Abort(
2168 _("patch %s does not exist") % filename)
2172 _("patch %s does not exist") % filename)
2169
2173
2170 if patchname:
2174 if patchname:
2171 self.checkpatchname(patchname, force)
2175 self.checkpatchname(patchname, force)
2172
2176
2173 self.ui.write(_('renaming %s to %s\n')
2177 self.ui.write(_('renaming %s to %s\n')
2174 % (filename, patchname))
2178 % (filename, patchname))
2175 util.rename(originpath, self.join(patchname))
2179 util.rename(originpath, self.join(patchname))
2176 else:
2180 else:
2177 patchname = filename
2181 patchname = filename
2178
2182
2179 else:
2183 else:
2180 if filename == '-' and not patchname:
2184 if filename == '-' and not patchname:
2181 raise error.Abort(_('need --name to import a patch from -'))
2185 raise error.Abort(_('need --name to import a patch from -'))
2182 elif not patchname:
2186 elif not patchname:
2183 patchname = normname(os.path.basename(filename.rstrip('/')))
2187 patchname = normname(os.path.basename(filename.rstrip('/')))
2184 self.checkpatchname(patchname, force)
2188 self.checkpatchname(patchname, force)
2185 try:
2189 try:
2186 if filename == '-':
2190 if filename == '-':
2187 text = self.ui.fin.read()
2191 text = self.ui.fin.read()
2188 else:
2192 else:
2189 fp = hg.openpath(self.ui, filename)
2193 fp = hg.openpath(self.ui, filename)
2190 text = fp.read()
2194 text = fp.read()
2191 fp.close()
2195 fp.close()
2192 except (OSError, IOError):
2196 except (OSError, IOError):
2193 raise error.Abort(_("unable to read file %s") % filename)
2197 raise error.Abort(_("unable to read file %s") % filename)
2194 patchf = self.opener(patchname, "w")
2198 patchf = self.opener(patchname, "w")
2195 patchf.write(text)
2199 patchf.write(text)
2196 patchf.close()
2200 patchf.close()
2197 if not force:
2201 if not force:
2198 checkseries(patchname)
2202 checkseries(patchname)
2199 if patchname not in self.series:
2203 if patchname not in self.series:
2200 index = self.fullseriesend() + i
2204 index = self.fullseriesend() + i
2201 self.fullseries[index:index] = [patchname]
2205 self.fullseries[index:index] = [patchname]
2202 self.parseseries()
2206 self.parseseries()
2203 self.seriesdirty = True
2207 self.seriesdirty = True
2204 self.ui.warn(_("adding %s to series file\n") % patchname)
2208 self.ui.warn(_("adding %s to series file\n") % patchname)
2205 self.added.append(patchname)
2209 self.added.append(patchname)
2206 imported.append(patchname)
2210 imported.append(patchname)
2207 patchname = None
2211 patchname = None
2208
2212
2209 self.removeundo(repo)
2213 self.removeundo(repo)
2210 return imported
2214 return imported
2211
2215
2212 def fixkeepchangesopts(ui, opts):
2216 def fixkeepchangesopts(ui, opts):
2213 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2217 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2214 or opts.get('exact')):
2218 or opts.get('exact')):
2215 return opts
2219 return opts
2216 opts = dict(opts)
2220 opts = dict(opts)
2217 opts['keep_changes'] = True
2221 opts['keep_changes'] = True
2218 return opts
2222 return opts
2219
2223
2220 @command("qdelete|qremove|qrm",
2224 @command("qdelete|qremove|qrm",
2221 [('k', 'keep', None, _('keep patch file')),
2225 [('k', 'keep', None, _('keep patch file')),
2222 ('r', 'rev', [],
2226 ('r', 'rev', [],
2223 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2227 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2224 _('hg qdelete [-k] [PATCH]...'))
2228 _('hg qdelete [-k] [PATCH]...'))
2225 def delete(ui, repo, *patches, **opts):
2229 def delete(ui, repo, *patches, **opts):
2226 """remove patches from queue
2230 """remove patches from queue
2227
2231
2228 The patches must not be applied, and at least one patch is required. Exact
2232 The patches must not be applied, and at least one patch is required. Exact
2229 patch identifiers must be given. With -k/--keep, the patch files are
2233 patch identifiers must be given. With -k/--keep, the patch files are
2230 preserved in the patch directory.
2234 preserved in the patch directory.
2231
2235
2232 To stop managing a patch and move it into permanent history,
2236 To stop managing a patch and move it into permanent history,
2233 use the :hg:`qfinish` command."""
2237 use the :hg:`qfinish` command."""
2234 q = repo.mq
2238 q = repo.mq
2235 q.delete(repo, patches, opts)
2239 q.delete(repo, patches, opts)
2236 q.savedirty()
2240 q.savedirty()
2237 return 0
2241 return 0
2238
2242
2239 @command("qapplied",
2243 @command("qapplied",
2240 [('1', 'last', None, _('show only the preceding applied patch'))
2244 [('1', 'last', None, _('show only the preceding applied patch'))
2241 ] + seriesopts,
2245 ] + seriesopts,
2242 _('hg qapplied [-1] [-s] [PATCH]'))
2246 _('hg qapplied [-1] [-s] [PATCH]'))
2243 def applied(ui, repo, patch=None, **opts):
2247 def applied(ui, repo, patch=None, **opts):
2244 """print the patches already applied
2248 """print the patches already applied
2245
2249
2246 Returns 0 on success."""
2250 Returns 0 on success."""
2247
2251
2248 q = repo.mq
2252 q = repo.mq
2249
2253
2250 if patch:
2254 if patch:
2251 if patch not in q.series:
2255 if patch not in q.series:
2252 raise error.Abort(_("patch %s is not in series file") % patch)
2256 raise error.Abort(_("patch %s is not in series file") % patch)
2253 end = q.series.index(patch) + 1
2257 end = q.series.index(patch) + 1
2254 else:
2258 else:
2255 end = q.seriesend(True)
2259 end = q.seriesend(True)
2256
2260
2257 if opts.get('last') and not end:
2261 if opts.get('last') and not end:
2258 ui.write(_("no patches applied\n"))
2262 ui.write(_("no patches applied\n"))
2259 return 1
2263 return 1
2260 elif opts.get('last') and end == 1:
2264 elif opts.get('last') and end == 1:
2261 ui.write(_("only one patch applied\n"))
2265 ui.write(_("only one patch applied\n"))
2262 return 1
2266 return 1
2263 elif opts.get('last'):
2267 elif opts.get('last'):
2264 start = end - 2
2268 start = end - 2
2265 end = 1
2269 end = 1
2266 else:
2270 else:
2267 start = 0
2271 start = 0
2268
2272
2269 q.qseries(repo, length=end, start=start, status='A',
2273 q.qseries(repo, length=end, start=start, status='A',
2270 summary=opts.get('summary'))
2274 summary=opts.get('summary'))
2271
2275
2272
2276
2273 @command("qunapplied",
2277 @command("qunapplied",
2274 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2278 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2275 _('hg qunapplied [-1] [-s] [PATCH]'))
2279 _('hg qunapplied [-1] [-s] [PATCH]'))
2276 def unapplied(ui, repo, patch=None, **opts):
2280 def unapplied(ui, repo, patch=None, **opts):
2277 """print the patches not yet applied
2281 """print the patches not yet applied
2278
2282
2279 Returns 0 on success."""
2283 Returns 0 on success."""
2280
2284
2281 q = repo.mq
2285 q = repo.mq
2282 if patch:
2286 if patch:
2283 if patch not in q.series:
2287 if patch not in q.series:
2284 raise error.Abort(_("patch %s is not in series file") % patch)
2288 raise error.Abort(_("patch %s is not in series file") % patch)
2285 start = q.series.index(patch) + 1
2289 start = q.series.index(patch) + 1
2286 else:
2290 else:
2287 start = q.seriesend(True)
2291 start = q.seriesend(True)
2288
2292
2289 if start == len(q.series) and opts.get('first'):
2293 if start == len(q.series) and opts.get('first'):
2290 ui.write(_("all patches applied\n"))
2294 ui.write(_("all patches applied\n"))
2291 return 1
2295 return 1
2292
2296
2293 if opts.get('first'):
2297 if opts.get('first'):
2294 length = 1
2298 length = 1
2295 else:
2299 else:
2296 length = None
2300 length = None
2297 q.qseries(repo, start=start, length=length, status='U',
2301 q.qseries(repo, start=start, length=length, status='U',
2298 summary=opts.get('summary'))
2302 summary=opts.get('summary'))
2299
2303
2300 @command("qimport",
2304 @command("qimport",
2301 [('e', 'existing', None, _('import file in patch directory')),
2305 [('e', 'existing', None, _('import file in patch directory')),
2302 ('n', 'name', '',
2306 ('n', 'name', '',
2303 _('name of patch file'), _('NAME')),
2307 _('name of patch file'), _('NAME')),
2304 ('f', 'force', None, _('overwrite existing files')),
2308 ('f', 'force', None, _('overwrite existing files')),
2305 ('r', 'rev', [],
2309 ('r', 'rev', [],
2306 _('place existing revisions under mq control'), _('REV')),
2310 _('place existing revisions under mq control'), _('REV')),
2307 ('g', 'git', None, _('use git extended diff format')),
2311 ('g', 'git', None, _('use git extended diff format')),
2308 ('P', 'push', None, _('qpush after importing'))],
2312 ('P', 'push', None, _('qpush after importing'))],
2309 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2313 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2310 def qimport(ui, repo, *filename, **opts):
2314 def qimport(ui, repo, *filename, **opts):
2311 """import a patch or existing changeset
2315 """import a patch or existing changeset
2312
2316
2313 The patch is inserted into the series after the last applied
2317 The patch is inserted into the series after the last applied
2314 patch. If no patches have been applied, qimport prepends the patch
2318 patch. If no patches have been applied, qimport prepends the patch
2315 to the series.
2319 to the series.
2316
2320
2317 The patch will have the same name as its source file unless you
2321 The patch will have the same name as its source file unless you
2318 give it a new one with -n/--name.
2322 give it a new one with -n/--name.
2319
2323
2320 You can register an existing patch inside the patch directory with
2324 You can register an existing patch inside the patch directory with
2321 the -e/--existing flag.
2325 the -e/--existing flag.
2322
2326
2323 With -f/--force, an existing patch of the same name will be
2327 With -f/--force, an existing patch of the same name will be
2324 overwritten.
2328 overwritten.
2325
2329
2326 An existing changeset may be placed under mq control with -r/--rev
2330 An existing changeset may be placed under mq control with -r/--rev
2327 (e.g. qimport --rev . -n patch will place the current revision
2331 (e.g. qimport --rev . -n patch will place the current revision
2328 under mq control). With -g/--git, patches imported with --rev will
2332 under mq control). With -g/--git, patches imported with --rev will
2329 use the git diff format. See the diffs help topic for information
2333 use the git diff format. See the diffs help topic for information
2330 on why this is important for preserving rename/copy information
2334 on why this is important for preserving rename/copy information
2331 and permission changes. Use :hg:`qfinish` to remove changesets
2335 and permission changes. Use :hg:`qfinish` to remove changesets
2332 from mq control.
2336 from mq control.
2333
2337
2334 To import a patch from standard input, pass - as the patch file.
2338 To import a patch from standard input, pass - as the patch file.
2335 When importing from standard input, a patch name must be specified
2339 When importing from standard input, a patch name must be specified
2336 using the --name flag.
2340 using the --name flag.
2337
2341
2338 To import an existing patch while renaming it::
2342 To import an existing patch while renaming it::
2339
2343
2340 hg qimport -e existing-patch -n new-name
2344 hg qimport -e existing-patch -n new-name
2341
2345
2342 Returns 0 if import succeeded.
2346 Returns 0 if import succeeded.
2343 """
2347 """
2344 with repo.lock(): # cause this may move phase
2348 with repo.lock(): # cause this may move phase
2345 q = repo.mq
2349 q = repo.mq
2346 try:
2350 try:
2347 imported = q.qimport(
2351 imported = q.qimport(
2348 repo, filename, patchname=opts.get('name'),
2352 repo, filename, patchname=opts.get('name'),
2349 existing=opts.get('existing'), force=opts.get('force'),
2353 existing=opts.get('existing'), force=opts.get('force'),
2350 rev=opts.get('rev'), git=opts.get('git'))
2354 rev=opts.get('rev'), git=opts.get('git'))
2351 finally:
2355 finally:
2352 q.savedirty()
2356 q.savedirty()
2353
2357
2354 if imported and opts.get('push') and not opts.get('rev'):
2358 if imported and opts.get('push') and not opts.get('rev'):
2355 return q.push(repo, imported[-1])
2359 return q.push(repo, imported[-1])
2356 return 0
2360 return 0
2357
2361
2358 def qinit(ui, repo, create):
2362 def qinit(ui, repo, create):
2359 """initialize a new queue repository
2363 """initialize a new queue repository
2360
2364
2361 This command also creates a series file for ordering patches, and
2365 This command also creates a series file for ordering patches, and
2362 an mq-specific .hgignore file in the queue repository, to exclude
2366 an mq-specific .hgignore file in the queue repository, to exclude
2363 the status and guards files (these contain mostly transient state).
2367 the status and guards files (these contain mostly transient state).
2364
2368
2365 Returns 0 if initialization succeeded."""
2369 Returns 0 if initialization succeeded."""
2366 q = repo.mq
2370 q = repo.mq
2367 r = q.init(repo, create)
2371 r = q.init(repo, create)
2368 q.savedirty()
2372 q.savedirty()
2369 if r:
2373 if r:
2370 if not os.path.exists(r.wjoin('.hgignore')):
2374 if not os.path.exists(r.wjoin('.hgignore')):
2371 fp = r.wvfs('.hgignore', 'w')
2375 fp = r.wvfs('.hgignore', 'w')
2372 fp.write('^\\.hg\n')
2376 fp.write('^\\.hg\n')
2373 fp.write('^\\.mq\n')
2377 fp.write('^\\.mq\n')
2374 fp.write('syntax: glob\n')
2378 fp.write('syntax: glob\n')
2375 fp.write('status\n')
2379 fp.write('status\n')
2376 fp.write('guards\n')
2380 fp.write('guards\n')
2377 fp.close()
2381 fp.close()
2378 if not os.path.exists(r.wjoin('series')):
2382 if not os.path.exists(r.wjoin('series')):
2379 r.wvfs('series', 'w').close()
2383 r.wvfs('series', 'w').close()
2380 r[None].add(['.hgignore', 'series'])
2384 r[None].add(['.hgignore', 'series'])
2381 commands.add(ui, r)
2385 commands.add(ui, r)
2382 return 0
2386 return 0
2383
2387
2384 @command("^qinit",
2388 @command("^qinit",
2385 [('c', 'create-repo', None, _('create queue repository'))],
2389 [('c', 'create-repo', None, _('create queue repository'))],
2386 _('hg qinit [-c]'))
2390 _('hg qinit [-c]'))
2387 def init(ui, repo, **opts):
2391 def init(ui, repo, **opts):
2388 """init a new queue repository (DEPRECATED)
2392 """init a new queue repository (DEPRECATED)
2389
2393
2390 The queue repository is unversioned by default. If
2394 The queue repository is unversioned by default. If
2391 -c/--create-repo is specified, qinit will create a separate nested
2395 -c/--create-repo is specified, qinit will create a separate nested
2392 repository for patches (qinit -c may also be run later to convert
2396 repository for patches (qinit -c may also be run later to convert
2393 an unversioned patch repository into a versioned one). You can use
2397 an unversioned patch repository into a versioned one). You can use
2394 qcommit to commit changes to this queue repository.
2398 qcommit to commit changes to this queue repository.
2395
2399
2396 This command is deprecated. Without -c, it's implied by other relevant
2400 This command is deprecated. Without -c, it's implied by other relevant
2397 commands. With -c, use :hg:`init --mq` instead."""
2401 commands. With -c, use :hg:`init --mq` instead."""
2398 return qinit(ui, repo, create=opts.get('create_repo'))
2402 return qinit(ui, repo, create=opts.get('create_repo'))
2399
2403
2400 @command("qclone",
2404 @command("qclone",
2401 [('', 'pull', None, _('use pull protocol to copy metadata')),
2405 [('', 'pull', None, _('use pull protocol to copy metadata')),
2402 ('U', 'noupdate', None,
2406 ('U', 'noupdate', None,
2403 _('do not update the new working directories')),
2407 _('do not update the new working directories')),
2404 ('', 'uncompressed', None,
2408 ('', 'uncompressed', None,
2405 _('use uncompressed transfer (fast over LAN)')),
2409 _('use uncompressed transfer (fast over LAN)')),
2406 ('p', 'patches', '',
2410 ('p', 'patches', '',
2407 _('location of source patch repository'), _('REPO')),
2411 _('location of source patch repository'), _('REPO')),
2408 ] + commands.remoteopts,
2412 ] + commands.remoteopts,
2409 _('hg qclone [OPTION]... SOURCE [DEST]'),
2413 _('hg qclone [OPTION]... SOURCE [DEST]'),
2410 norepo=True)
2414 norepo=True)
2411 def clone(ui, source, dest=None, **opts):
2415 def clone(ui, source, dest=None, **opts):
2412 '''clone main and patch repository at same time
2416 '''clone main and patch repository at same time
2413
2417
2414 If source is local, destination will have no patches applied. If
2418 If source is local, destination will have no patches applied. If
2415 source is remote, this command can not check if patches are
2419 source is remote, this command can not check if patches are
2416 applied in source, so cannot guarantee that patches are not
2420 applied in source, so cannot guarantee that patches are not
2417 applied in destination. If you clone remote repository, be sure
2421 applied in destination. If you clone remote repository, be sure
2418 before that it has no patches applied.
2422 before that it has no patches applied.
2419
2423
2420 Source patch repository is looked for in <src>/.hg/patches by
2424 Source patch repository is looked for in <src>/.hg/patches by
2421 default. Use -p <url> to change.
2425 default. Use -p <url> to change.
2422
2426
2423 The patch directory must be a nested Mercurial repository, as
2427 The patch directory must be a nested Mercurial repository, as
2424 would be created by :hg:`init --mq`.
2428 would be created by :hg:`init --mq`.
2425
2429
2426 Return 0 on success.
2430 Return 0 on success.
2427 '''
2431 '''
2428 def patchdir(repo):
2432 def patchdir(repo):
2429 """compute a patch repo url from a repo object"""
2433 """compute a patch repo url from a repo object"""
2430 url = repo.url()
2434 url = repo.url()
2431 if url.endswith('/'):
2435 if url.endswith('/'):
2432 url = url[:-1]
2436 url = url[:-1]
2433 return url + '/.hg/patches'
2437 return url + '/.hg/patches'
2434
2438
2435 # main repo (destination and sources)
2439 # main repo (destination and sources)
2436 if dest is None:
2440 if dest is None:
2437 dest = hg.defaultdest(source)
2441 dest = hg.defaultdest(source)
2438 sr = hg.peer(ui, opts, ui.expandpath(source))
2442 sr = hg.peer(ui, opts, ui.expandpath(source))
2439
2443
2440 # patches repo (source only)
2444 # patches repo (source only)
2441 if opts.get('patches'):
2445 if opts.get('patches'):
2442 patchespath = ui.expandpath(opts.get('patches'))
2446 patchespath = ui.expandpath(opts.get('patches'))
2443 else:
2447 else:
2444 patchespath = patchdir(sr)
2448 patchespath = patchdir(sr)
2445 try:
2449 try:
2446 hg.peer(ui, opts, patchespath)
2450 hg.peer(ui, opts, patchespath)
2447 except error.RepoError:
2451 except error.RepoError:
2448 raise error.Abort(_('versioned patch repository not found'
2452 raise error.Abort(_('versioned patch repository not found'
2449 ' (see init --mq)'))
2453 ' (see init --mq)'))
2450 qbase, destrev = None, None
2454 qbase, destrev = None, None
2451 if sr.local():
2455 if sr.local():
2452 repo = sr.local()
2456 repo = sr.local()
2453 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2457 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2454 qbase = repo.mq.applied[0].node
2458 qbase = repo.mq.applied[0].node
2455 if not hg.islocal(dest):
2459 if not hg.islocal(dest):
2456 heads = set(repo.heads())
2460 heads = set(repo.heads())
2457 destrev = list(heads.difference(repo.heads(qbase)))
2461 destrev = list(heads.difference(repo.heads(qbase)))
2458 destrev.append(repo.changelog.parents(qbase)[0])
2462 destrev.append(repo.changelog.parents(qbase)[0])
2459 elif sr.capable('lookup'):
2463 elif sr.capable('lookup'):
2460 try:
2464 try:
2461 qbase = sr.lookup('qbase')
2465 qbase = sr.lookup('qbase')
2462 except error.RepoError:
2466 except error.RepoError:
2463 pass
2467 pass
2464
2468
2465 ui.note(_('cloning main repository\n'))
2469 ui.note(_('cloning main repository\n'))
2466 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2470 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2467 pull=opts.get('pull'),
2471 pull=opts.get('pull'),
2468 rev=destrev,
2472 rev=destrev,
2469 update=False,
2473 update=False,
2470 stream=opts.get('uncompressed'))
2474 stream=opts.get('uncompressed'))
2471
2475
2472 ui.note(_('cloning patch repository\n'))
2476 ui.note(_('cloning patch repository\n'))
2473 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2477 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2474 pull=opts.get('pull'), update=not opts.get('noupdate'),
2478 pull=opts.get('pull'), update=not opts.get('noupdate'),
2475 stream=opts.get('uncompressed'))
2479 stream=opts.get('uncompressed'))
2476
2480
2477 if dr.local():
2481 if dr.local():
2478 repo = dr.local()
2482 repo = dr.local()
2479 if qbase:
2483 if qbase:
2480 ui.note(_('stripping applied patches from destination '
2484 ui.note(_('stripping applied patches from destination '
2481 'repository\n'))
2485 'repository\n'))
2482 strip(ui, repo, [qbase], update=False, backup=None)
2486 strip(ui, repo, [qbase], update=False, backup=None)
2483 if not opts.get('noupdate'):
2487 if not opts.get('noupdate'):
2484 ui.note(_('updating destination repository\n'))
2488 ui.note(_('updating destination repository\n'))
2485 hg.update(repo, repo.changelog.tip())
2489 hg.update(repo, repo.changelog.tip())
2486
2490
2487 @command("qcommit|qci",
2491 @command("qcommit|qci",
2488 commands.table["^commit|ci"][1],
2492 commands.table["^commit|ci"][1],
2489 _('hg qcommit [OPTION]... [FILE]...'),
2493 _('hg qcommit [OPTION]... [FILE]...'),
2490 inferrepo=True)
2494 inferrepo=True)
2491 def commit(ui, repo, *pats, **opts):
2495 def commit(ui, repo, *pats, **opts):
2492 """commit changes in the queue repository (DEPRECATED)
2496 """commit changes in the queue repository (DEPRECATED)
2493
2497
2494 This command is deprecated; use :hg:`commit --mq` instead."""
2498 This command is deprecated; use :hg:`commit --mq` instead."""
2495 q = repo.mq
2499 q = repo.mq
2496 r = q.qrepo()
2500 r = q.qrepo()
2497 if not r:
2501 if not r:
2498 raise error.Abort('no queue repository')
2502 raise error.Abort('no queue repository')
2499 commands.commit(r.ui, r, *pats, **opts)
2503 commands.commit(r.ui, r, *pats, **opts)
2500
2504
2501 @command("qseries",
2505 @command("qseries",
2502 [('m', 'missing', None, _('print patches not in series')),
2506 [('m', 'missing', None, _('print patches not in series')),
2503 ] + seriesopts,
2507 ] + seriesopts,
2504 _('hg qseries [-ms]'))
2508 _('hg qseries [-ms]'))
2505 def series(ui, repo, **opts):
2509 def series(ui, repo, **opts):
2506 """print the entire series file
2510 """print the entire series file
2507
2511
2508 Returns 0 on success."""
2512 Returns 0 on success."""
2509 repo.mq.qseries(repo, missing=opts.get('missing'),
2513 repo.mq.qseries(repo, missing=opts.get('missing'),
2510 summary=opts.get('summary'))
2514 summary=opts.get('summary'))
2511 return 0
2515 return 0
2512
2516
2513 @command("qtop", seriesopts, _('hg qtop [-s]'))
2517 @command("qtop", seriesopts, _('hg qtop [-s]'))
2514 def top(ui, repo, **opts):
2518 def top(ui, repo, **opts):
2515 """print the name of the current patch
2519 """print the name of the current patch
2516
2520
2517 Returns 0 on success."""
2521 Returns 0 on success."""
2518 q = repo.mq
2522 q = repo.mq
2519 if q.applied:
2523 if q.applied:
2520 t = q.seriesend(True)
2524 t = q.seriesend(True)
2521 else:
2525 else:
2522 t = 0
2526 t = 0
2523
2527
2524 if t:
2528 if t:
2525 q.qseries(repo, start=t - 1, length=1, status='A',
2529 q.qseries(repo, start=t - 1, length=1, status='A',
2526 summary=opts.get('summary'))
2530 summary=opts.get('summary'))
2527 else:
2531 else:
2528 ui.write(_("no patches applied\n"))
2532 ui.write(_("no patches applied\n"))
2529 return 1
2533 return 1
2530
2534
2531 @command("qnext", seriesopts, _('hg qnext [-s]'))
2535 @command("qnext", seriesopts, _('hg qnext [-s]'))
2532 def next(ui, repo, **opts):
2536 def next(ui, repo, **opts):
2533 """print the name of the next pushable patch
2537 """print the name of the next pushable patch
2534
2538
2535 Returns 0 on success."""
2539 Returns 0 on success."""
2536 q = repo.mq
2540 q = repo.mq
2537 end = q.seriesend()
2541 end = q.seriesend()
2538 if end == len(q.series):
2542 if end == len(q.series):
2539 ui.write(_("all patches applied\n"))
2543 ui.write(_("all patches applied\n"))
2540 return 1
2544 return 1
2541 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2545 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2542
2546
2543 @command("qprev", seriesopts, _('hg qprev [-s]'))
2547 @command("qprev", seriesopts, _('hg qprev [-s]'))
2544 def prev(ui, repo, **opts):
2548 def prev(ui, repo, **opts):
2545 """print the name of the preceding applied patch
2549 """print the name of the preceding applied patch
2546
2550
2547 Returns 0 on success."""
2551 Returns 0 on success."""
2548 q = repo.mq
2552 q = repo.mq
2549 l = len(q.applied)
2553 l = len(q.applied)
2550 if l == 1:
2554 if l == 1:
2551 ui.write(_("only one patch applied\n"))
2555 ui.write(_("only one patch applied\n"))
2552 return 1
2556 return 1
2553 if not l:
2557 if not l:
2554 ui.write(_("no patches applied\n"))
2558 ui.write(_("no patches applied\n"))
2555 return 1
2559 return 1
2556 idx = q.series.index(q.applied[-2].name)
2560 idx = q.series.index(q.applied[-2].name)
2557 q.qseries(repo, start=idx, length=1, status='A',
2561 q.qseries(repo, start=idx, length=1, status='A',
2558 summary=opts.get('summary'))
2562 summary=opts.get('summary'))
2559
2563
2560 def setupheaderopts(ui, opts):
2564 def setupheaderopts(ui, opts):
2561 if not opts.get('user') and opts.get('currentuser'):
2565 if not opts.get('user') and opts.get('currentuser'):
2562 opts['user'] = ui.username()
2566 opts['user'] = ui.username()
2563 if not opts.get('date') and opts.get('currentdate'):
2567 if not opts.get('date') and opts.get('currentdate'):
2564 opts['date'] = "%d %d" % util.makedate()
2568 opts['date'] = "%d %d" % util.makedate()
2565
2569
2566 @command("^qnew",
2570 @command("^qnew",
2567 [('e', 'edit', None, _('invoke editor on commit messages')),
2571 [('e', 'edit', None, _('invoke editor on commit messages')),
2568 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2572 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2569 ('g', 'git', None, _('use git extended diff format')),
2573 ('g', 'git', None, _('use git extended diff format')),
2570 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2574 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2571 ('u', 'user', '',
2575 ('u', 'user', '',
2572 _('add "From: <USER>" to patch'), _('USER')),
2576 _('add "From: <USER>" to patch'), _('USER')),
2573 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2577 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2574 ('d', 'date', '',
2578 ('d', 'date', '',
2575 _('add "Date: <DATE>" to patch'), _('DATE'))
2579 _('add "Date: <DATE>" to patch'), _('DATE'))
2576 ] + commands.walkopts + commands.commitopts,
2580 ] + commands.walkopts + commands.commitopts,
2577 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2581 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2578 inferrepo=True)
2582 inferrepo=True)
2579 def new(ui, repo, patch, *args, **opts):
2583 def new(ui, repo, patch, *args, **opts):
2580 """create a new patch
2584 """create a new patch
2581
2585
2582 qnew creates a new patch on top of the currently-applied patch (if
2586 qnew creates a new patch on top of the currently-applied patch (if
2583 any). The patch will be initialized with any outstanding changes
2587 any). The patch will be initialized with any outstanding changes
2584 in the working directory. You may also use -I/--include,
2588 in the working directory. You may also use -I/--include,
2585 -X/--exclude, and/or a list of files after the patch name to add
2589 -X/--exclude, and/or a list of files after the patch name to add
2586 only changes to matching files to the new patch, leaving the rest
2590 only changes to matching files to the new patch, leaving the rest
2587 as uncommitted modifications.
2591 as uncommitted modifications.
2588
2592
2589 -u/--user and -d/--date can be used to set the (given) user and
2593 -u/--user and -d/--date can be used to set the (given) user and
2590 date, respectively. -U/--currentuser and -D/--currentdate set user
2594 date, respectively. -U/--currentuser and -D/--currentdate set user
2591 to current user and date to current date.
2595 to current user and date to current date.
2592
2596
2593 -e/--edit, -m/--message or -l/--logfile set the patch header as
2597 -e/--edit, -m/--message or -l/--logfile set the patch header as
2594 well as the commit message. If none is specified, the header is
2598 well as the commit message. If none is specified, the header is
2595 empty and the commit message is '[mq]: PATCH'.
2599 empty and the commit message is '[mq]: PATCH'.
2596
2600
2597 Use the -g/--git option to keep the patch in the git extended diff
2601 Use the -g/--git option to keep the patch in the git extended diff
2598 format. Read the diffs help topic for more information on why this
2602 format. Read the diffs help topic for more information on why this
2599 is important for preserving permission changes and copy/rename
2603 is important for preserving permission changes and copy/rename
2600 information.
2604 information.
2601
2605
2602 Returns 0 on successful creation of a new patch.
2606 Returns 0 on successful creation of a new patch.
2603 """
2607 """
2604 msg = cmdutil.logmessage(ui, opts)
2608 msg = cmdutil.logmessage(ui, opts)
2605 q = repo.mq
2609 q = repo.mq
2606 opts['msg'] = msg
2610 opts['msg'] = msg
2607 setupheaderopts(ui, opts)
2611 setupheaderopts(ui, opts)
2608 q.new(repo, patch, *args, **opts)
2612 q.new(repo, patch, *args, **opts)
2609 q.savedirty()
2613 q.savedirty()
2610 return 0
2614 return 0
2611
2615
2612 @command("^qrefresh",
2616 @command("^qrefresh",
2613 [('e', 'edit', None, _('invoke editor on commit messages')),
2617 [('e', 'edit', None, _('invoke editor on commit messages')),
2614 ('g', 'git', None, _('use git extended diff format')),
2618 ('g', 'git', None, _('use git extended diff format')),
2615 ('s', 'short', None,
2619 ('s', 'short', None,
2616 _('refresh only files already in the patch and specified files')),
2620 _('refresh only files already in the patch and specified files')),
2617 ('U', 'currentuser', None,
2621 ('U', 'currentuser', None,
2618 _('add/update author field in patch with current user')),
2622 _('add/update author field in patch with current user')),
2619 ('u', 'user', '',
2623 ('u', 'user', '',
2620 _('add/update author field in patch with given user'), _('USER')),
2624 _('add/update author field in patch with given user'), _('USER')),
2621 ('D', 'currentdate', None,
2625 ('D', 'currentdate', None,
2622 _('add/update date field in patch with current date')),
2626 _('add/update date field in patch with current date')),
2623 ('d', 'date', '',
2627 ('d', 'date', '',
2624 _('add/update date field in patch with given date'), _('DATE'))
2628 _('add/update date field in patch with given date'), _('DATE'))
2625 ] + commands.walkopts + commands.commitopts,
2629 ] + commands.walkopts + commands.commitopts,
2626 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2630 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2627 inferrepo=True)
2631 inferrepo=True)
2628 def refresh(ui, repo, *pats, **opts):
2632 def refresh(ui, repo, *pats, **opts):
2629 """update the current patch
2633 """update the current patch
2630
2634
2631 If any file patterns are provided, the refreshed patch will
2635 If any file patterns are provided, the refreshed patch will
2632 contain only the modifications that match those patterns; the
2636 contain only the modifications that match those patterns; the
2633 remaining modifications will remain in the working directory.
2637 remaining modifications will remain in the working directory.
2634
2638
2635 If -s/--short is specified, files currently included in the patch
2639 If -s/--short is specified, files currently included in the patch
2636 will be refreshed just like matched files and remain in the patch.
2640 will be refreshed just like matched files and remain in the patch.
2637
2641
2638 If -e/--edit is specified, Mercurial will start your configured editor for
2642 If -e/--edit is specified, Mercurial will start your configured editor for
2639 you to enter a message. In case qrefresh fails, you will find a backup of
2643 you to enter a message. In case qrefresh fails, you will find a backup of
2640 your message in ``.hg/last-message.txt``.
2644 your message in ``.hg/last-message.txt``.
2641
2645
2642 hg add/remove/copy/rename work as usual, though you might want to
2646 hg add/remove/copy/rename work as usual, though you might want to
2643 use git-style patches (-g/--git or [diff] git=1) to track copies
2647 use git-style patches (-g/--git or [diff] git=1) to track copies
2644 and renames. See the diffs help topic for more information on the
2648 and renames. See the diffs help topic for more information on the
2645 git diff format.
2649 git diff format.
2646
2650
2647 Returns 0 on success.
2651 Returns 0 on success.
2648 """
2652 """
2649 q = repo.mq
2653 q = repo.mq
2650 message = cmdutil.logmessage(ui, opts)
2654 message = cmdutil.logmessage(ui, opts)
2651 setupheaderopts(ui, opts)
2655 setupheaderopts(ui, opts)
2652 with repo.wlock():
2656 with repo.wlock():
2653 ret = q.refresh(repo, pats, msg=message, **opts)
2657 ret = q.refresh(repo, pats, msg=message, **opts)
2654 q.savedirty()
2658 q.savedirty()
2655 return ret
2659 return ret
2656
2660
2657 @command("^qdiff",
2661 @command("^qdiff",
2658 commands.diffopts + commands.diffopts2 + commands.walkopts,
2662 commands.diffopts + commands.diffopts2 + commands.walkopts,
2659 _('hg qdiff [OPTION]... [FILE]...'),
2663 _('hg qdiff [OPTION]... [FILE]...'),
2660 inferrepo=True)
2664 inferrepo=True)
2661 def diff(ui, repo, *pats, **opts):
2665 def diff(ui, repo, *pats, **opts):
2662 """diff of the current patch and subsequent modifications
2666 """diff of the current patch and subsequent modifications
2663
2667
2664 Shows a diff which includes the current patch as well as any
2668 Shows a diff which includes the current patch as well as any
2665 changes which have been made in the working directory since the
2669 changes which have been made in the working directory since the
2666 last refresh (thus showing what the current patch would become
2670 last refresh (thus showing what the current patch would become
2667 after a qrefresh).
2671 after a qrefresh).
2668
2672
2669 Use :hg:`diff` if you only want to see the changes made since the
2673 Use :hg:`diff` if you only want to see the changes made since the
2670 last qrefresh, or :hg:`export qtip` if you want to see changes
2674 last qrefresh, or :hg:`export qtip` if you want to see changes
2671 made by the current patch without including changes made since the
2675 made by the current patch without including changes made since the
2672 qrefresh.
2676 qrefresh.
2673
2677
2674 Returns 0 on success.
2678 Returns 0 on success.
2675 """
2679 """
2676 ui.pager('qdiff')
2680 ui.pager('qdiff')
2677 repo.mq.diff(repo, pats, opts)
2681 repo.mq.diff(repo, pats, opts)
2678 return 0
2682 return 0
2679
2683
2680 @command('qfold',
2684 @command('qfold',
2681 [('e', 'edit', None, _('invoke editor on commit messages')),
2685 [('e', 'edit', None, _('invoke editor on commit messages')),
2682 ('k', 'keep', None, _('keep folded patch files')),
2686 ('k', 'keep', None, _('keep folded patch files')),
2683 ] + commands.commitopts,
2687 ] + commands.commitopts,
2684 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2688 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2685 def fold(ui, repo, *files, **opts):
2689 def fold(ui, repo, *files, **opts):
2686 """fold the named patches into the current patch
2690 """fold the named patches into the current patch
2687
2691
2688 Patches must not yet be applied. Each patch will be successively
2692 Patches must not yet be applied. Each patch will be successively
2689 applied to the current patch in the order given. If all the
2693 applied to the current patch in the order given. If all the
2690 patches apply successfully, the current patch will be refreshed
2694 patches apply successfully, the current patch will be refreshed
2691 with the new cumulative patch, and the folded patches will be
2695 with the new cumulative patch, and the folded patches will be
2692 deleted. With -k/--keep, the folded patch files will not be
2696 deleted. With -k/--keep, the folded patch files will not be
2693 removed afterwards.
2697 removed afterwards.
2694
2698
2695 The header for each folded patch will be concatenated with the
2699 The header for each folded patch will be concatenated with the
2696 current patch header, separated by a line of ``* * *``.
2700 current patch header, separated by a line of ``* * *``.
2697
2701
2698 Returns 0 on success."""
2702 Returns 0 on success."""
2699 q = repo.mq
2703 q = repo.mq
2700 if not files:
2704 if not files:
2701 raise error.Abort(_('qfold requires at least one patch name'))
2705 raise error.Abort(_('qfold requires at least one patch name'))
2702 if not q.checktoppatch(repo)[0]:
2706 if not q.checktoppatch(repo)[0]:
2703 raise error.Abort(_('no patches applied'))
2707 raise error.Abort(_('no patches applied'))
2704 q.checklocalchanges(repo)
2708 q.checklocalchanges(repo)
2705
2709
2706 message = cmdutil.logmessage(ui, opts)
2710 message = cmdutil.logmessage(ui, opts)
2707
2711
2708 parent = q.lookup('qtip')
2712 parent = q.lookup('qtip')
2709 patches = []
2713 patches = []
2710 messages = []
2714 messages = []
2711 for f in files:
2715 for f in files:
2712 p = q.lookup(f)
2716 p = q.lookup(f)
2713 if p in patches or p == parent:
2717 if p in patches or p == parent:
2714 ui.warn(_('skipping already folded patch %s\n') % p)
2718 ui.warn(_('skipping already folded patch %s\n') % p)
2715 if q.isapplied(p):
2719 if q.isapplied(p):
2716 raise error.Abort(_('qfold cannot fold already applied patch %s')
2720 raise error.Abort(_('qfold cannot fold already applied patch %s')
2717 % p)
2721 % p)
2718 patches.append(p)
2722 patches.append(p)
2719
2723
2720 for p in patches:
2724 for p in patches:
2721 if not message:
2725 if not message:
2722 ph = patchheader(q.join(p), q.plainmode)
2726 ph = patchheader(q.join(p), q.plainmode)
2723 if ph.message:
2727 if ph.message:
2724 messages.append(ph.message)
2728 messages.append(ph.message)
2725 pf = q.join(p)
2729 pf = q.join(p)
2726 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2730 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2727 if not patchsuccess:
2731 if not patchsuccess:
2728 raise error.Abort(_('error folding patch %s') % p)
2732 raise error.Abort(_('error folding patch %s') % p)
2729
2733
2730 if not message:
2734 if not message:
2731 ph = patchheader(q.join(parent), q.plainmode)
2735 ph = patchheader(q.join(parent), q.plainmode)
2732 message = ph.message
2736 message = ph.message
2733 for msg in messages:
2737 for msg in messages:
2734 if msg:
2738 if msg:
2735 if message:
2739 if message:
2736 message.append('* * *')
2740 message.append('* * *')
2737 message.extend(msg)
2741 message.extend(msg)
2738 message = '\n'.join(message)
2742 message = '\n'.join(message)
2739
2743
2740 diffopts = q.patchopts(q.diffopts(), *patches)
2744 diffopts = q.patchopts(q.diffopts(), *patches)
2741 with repo.wlock():
2745 with repo.wlock():
2742 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2746 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2743 editform='mq.qfold')
2747 editform='mq.qfold')
2744 q.delete(repo, patches, opts)
2748 q.delete(repo, patches, opts)
2745 q.savedirty()
2749 q.savedirty()
2746
2750
2747 @command("qgoto",
2751 @command("qgoto",
2748 [('', 'keep-changes', None,
2752 [('', 'keep-changes', None,
2749 _('tolerate non-conflicting local changes')),
2753 _('tolerate non-conflicting local changes')),
2750 ('f', 'force', None, _('overwrite any local changes')),
2754 ('f', 'force', None, _('overwrite any local changes')),
2751 ('', 'no-backup', None, _('do not save backup copies of files'))],
2755 ('', 'no-backup', None, _('do not save backup copies of files'))],
2752 _('hg qgoto [OPTION]... PATCH'))
2756 _('hg qgoto [OPTION]... PATCH'))
2753 def goto(ui, repo, patch, **opts):
2757 def goto(ui, repo, patch, **opts):
2754 '''push or pop patches until named patch is at top of stack
2758 '''push or pop patches until named patch is at top of stack
2755
2759
2756 Returns 0 on success.'''
2760 Returns 0 on success.'''
2757 opts = fixkeepchangesopts(ui, opts)
2761 opts = fixkeepchangesopts(ui, opts)
2758 q = repo.mq
2762 q = repo.mq
2759 patch = q.lookup(patch)
2763 patch = q.lookup(patch)
2760 nobackup = opts.get('no_backup')
2764 nobackup = opts.get('no_backup')
2761 keepchanges = opts.get('keep_changes')
2765 keepchanges = opts.get('keep_changes')
2762 if q.isapplied(patch):
2766 if q.isapplied(patch):
2763 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2767 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2764 keepchanges=keepchanges)
2768 keepchanges=keepchanges)
2765 else:
2769 else:
2766 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2770 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2767 keepchanges=keepchanges)
2771 keepchanges=keepchanges)
2768 q.savedirty()
2772 q.savedirty()
2769 return ret
2773 return ret
2770
2774
2771 @command("qguard",
2775 @command("qguard",
2772 [('l', 'list', None, _('list all patches and guards')),
2776 [('l', 'list', None, _('list all patches and guards')),
2773 ('n', 'none', None, _('drop all guards'))],
2777 ('n', 'none', None, _('drop all guards'))],
2774 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2778 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2775 def guard(ui, repo, *args, **opts):
2779 def guard(ui, repo, *args, **opts):
2776 '''set or print guards for a patch
2780 '''set or print guards for a patch
2777
2781
2778 Guards control whether a patch can be pushed. A patch with no
2782 Guards control whether a patch can be pushed. A patch with no
2779 guards is always pushed. A patch with a positive guard ("+foo") is
2783 guards is always pushed. A patch with a positive guard ("+foo") is
2780 pushed only if the :hg:`qselect` command has activated it. A patch with
2784 pushed only if the :hg:`qselect` command has activated it. A patch with
2781 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2785 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2782 has activated it.
2786 has activated it.
2783
2787
2784 With no arguments, print the currently active guards.
2788 With no arguments, print the currently active guards.
2785 With arguments, set guards for the named patch.
2789 With arguments, set guards for the named patch.
2786
2790
2787 .. note::
2791 .. note::
2788
2792
2789 Specifying negative guards now requires '--'.
2793 Specifying negative guards now requires '--'.
2790
2794
2791 To set guards on another patch::
2795 To set guards on another patch::
2792
2796
2793 hg qguard other.patch -- +2.6.17 -stable
2797 hg qguard other.patch -- +2.6.17 -stable
2794
2798
2795 Returns 0 on success.
2799 Returns 0 on success.
2796 '''
2800 '''
2797 def status(idx):
2801 def status(idx):
2798 guards = q.seriesguards[idx] or ['unguarded']
2802 guards = q.seriesguards[idx] or ['unguarded']
2799 if q.series[idx] in applied:
2803 if q.series[idx] in applied:
2800 state = 'applied'
2804 state = 'applied'
2801 elif q.pushable(idx)[0]:
2805 elif q.pushable(idx)[0]:
2802 state = 'unapplied'
2806 state = 'unapplied'
2803 else:
2807 else:
2804 state = 'guarded'
2808 state = 'guarded'
2805 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2809 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2806 ui.write('%s: ' % ui.label(q.series[idx], label))
2810 ui.write('%s: ' % ui.label(q.series[idx], label))
2807
2811
2808 for i, guard in enumerate(guards):
2812 for i, guard in enumerate(guards):
2809 if guard.startswith('+'):
2813 if guard.startswith('+'):
2810 ui.write(guard, label='qguard.positive')
2814 ui.write(guard, label='qguard.positive')
2811 elif guard.startswith('-'):
2815 elif guard.startswith('-'):
2812 ui.write(guard, label='qguard.negative')
2816 ui.write(guard, label='qguard.negative')
2813 else:
2817 else:
2814 ui.write(guard, label='qguard.unguarded')
2818 ui.write(guard, label='qguard.unguarded')
2815 if i != len(guards) - 1:
2819 if i != len(guards) - 1:
2816 ui.write(' ')
2820 ui.write(' ')
2817 ui.write('\n')
2821 ui.write('\n')
2818 q = repo.mq
2822 q = repo.mq
2819 applied = set(p.name for p in q.applied)
2823 applied = set(p.name for p in q.applied)
2820 patch = None
2824 patch = None
2821 args = list(args)
2825 args = list(args)
2822 if opts.get('list'):
2826 if opts.get('list'):
2823 if args or opts.get('none'):
2827 if args or opts.get('none'):
2824 raise error.Abort(_('cannot mix -l/--list with options or '
2828 raise error.Abort(_('cannot mix -l/--list with options or '
2825 'arguments'))
2829 'arguments'))
2826 for i in xrange(len(q.series)):
2830 for i in xrange(len(q.series)):
2827 status(i)
2831 status(i)
2828 return
2832 return
2829 if not args or args[0][0:1] in '-+':
2833 if not args or args[0][0:1] in '-+':
2830 if not q.applied:
2834 if not q.applied:
2831 raise error.Abort(_('no patches applied'))
2835 raise error.Abort(_('no patches applied'))
2832 patch = q.applied[-1].name
2836 patch = q.applied[-1].name
2833 if patch is None and args[0][0:1] not in '-+':
2837 if patch is None and args[0][0:1] not in '-+':
2834 patch = args.pop(0)
2838 patch = args.pop(0)
2835 if patch is None:
2839 if patch is None:
2836 raise error.Abort(_('no patch to work with'))
2840 raise error.Abort(_('no patch to work with'))
2837 if args or opts.get('none'):
2841 if args or opts.get('none'):
2838 idx = q.findseries(patch)
2842 idx = q.findseries(patch)
2839 if idx is None:
2843 if idx is None:
2840 raise error.Abort(_('no patch named %s') % patch)
2844 raise error.Abort(_('no patch named %s') % patch)
2841 q.setguards(idx, args)
2845 q.setguards(idx, args)
2842 q.savedirty()
2846 q.savedirty()
2843 else:
2847 else:
2844 status(q.series.index(q.lookup(patch)))
2848 status(q.series.index(q.lookup(patch)))
2845
2849
2846 @command("qheader", [], _('hg qheader [PATCH]'))
2850 @command("qheader", [], _('hg qheader [PATCH]'))
2847 def header(ui, repo, patch=None):
2851 def header(ui, repo, patch=None):
2848 """print the header of the topmost or specified patch
2852 """print the header of the topmost or specified patch
2849
2853
2850 Returns 0 on success."""
2854 Returns 0 on success."""
2851 q = repo.mq
2855 q = repo.mq
2852
2856
2853 if patch:
2857 if patch:
2854 patch = q.lookup(patch)
2858 patch = q.lookup(patch)
2855 else:
2859 else:
2856 if not q.applied:
2860 if not q.applied:
2857 ui.write(_('no patches applied\n'))
2861 ui.write(_('no patches applied\n'))
2858 return 1
2862 return 1
2859 patch = q.lookup('qtip')
2863 patch = q.lookup('qtip')
2860 ph = patchheader(q.join(patch), q.plainmode)
2864 ph = patchheader(q.join(patch), q.plainmode)
2861
2865
2862 ui.write('\n'.join(ph.message) + '\n')
2866 ui.write('\n'.join(ph.message) + '\n')
2863
2867
2864 def lastsavename(path):
2868 def lastsavename(path):
2865 (directory, base) = os.path.split(path)
2869 (directory, base) = os.path.split(path)
2866 names = os.listdir(directory)
2870 names = os.listdir(directory)
2867 namere = re.compile("%s.([0-9]+)" % base)
2871 namere = re.compile("%s.([0-9]+)" % base)
2868 maxindex = None
2872 maxindex = None
2869 maxname = None
2873 maxname = None
2870 for f in names:
2874 for f in names:
2871 m = namere.match(f)
2875 m = namere.match(f)
2872 if m:
2876 if m:
2873 index = int(m.group(1))
2877 index = int(m.group(1))
2874 if maxindex is None or index > maxindex:
2878 if maxindex is None or index > maxindex:
2875 maxindex = index
2879 maxindex = index
2876 maxname = f
2880 maxname = f
2877 if maxname:
2881 if maxname:
2878 return (os.path.join(directory, maxname), maxindex)
2882 return (os.path.join(directory, maxname), maxindex)
2879 return (None, None)
2883 return (None, None)
2880
2884
2881 def savename(path):
2885 def savename(path):
2882 (last, index) = lastsavename(path)
2886 (last, index) = lastsavename(path)
2883 if last is None:
2887 if last is None:
2884 index = 0
2888 index = 0
2885 newpath = path + ".%d" % (index + 1)
2889 newpath = path + ".%d" % (index + 1)
2886 return newpath
2890 return newpath
2887
2891
2888 @command("^qpush",
2892 @command("^qpush",
2889 [('', 'keep-changes', None,
2893 [('', 'keep-changes', None,
2890 _('tolerate non-conflicting local changes')),
2894 _('tolerate non-conflicting local changes')),
2891 ('f', 'force', None, _('apply on top of local changes')),
2895 ('f', 'force', None, _('apply on top of local changes')),
2892 ('e', 'exact', None,
2896 ('e', 'exact', None,
2893 _('apply the target patch to its recorded parent')),
2897 _('apply the target patch to its recorded parent')),
2894 ('l', 'list', None, _('list patch name in commit text')),
2898 ('l', 'list', None, _('list patch name in commit text')),
2895 ('a', 'all', None, _('apply all patches')),
2899 ('a', 'all', None, _('apply all patches')),
2896 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2900 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2897 ('n', 'name', '',
2901 ('n', 'name', '',
2898 _('merge queue name (DEPRECATED)'), _('NAME')),
2902 _('merge queue name (DEPRECATED)'), _('NAME')),
2899 ('', 'move', None,
2903 ('', 'move', None,
2900 _('reorder patch series and apply only the patch')),
2904 _('reorder patch series and apply only the patch')),
2901 ('', 'no-backup', None, _('do not save backup copies of files'))],
2905 ('', 'no-backup', None, _('do not save backup copies of files'))],
2902 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2906 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2903 def push(ui, repo, patch=None, **opts):
2907 def push(ui, repo, patch=None, **opts):
2904 """push the next patch onto the stack
2908 """push the next patch onto the stack
2905
2909
2906 By default, abort if the working directory contains uncommitted
2910 By default, abort if the working directory contains uncommitted
2907 changes. With --keep-changes, abort only if the uncommitted files
2911 changes. With --keep-changes, abort only if the uncommitted files
2908 overlap with patched files. With -f/--force, backup and patch over
2912 overlap with patched files. With -f/--force, backup and patch over
2909 uncommitted changes.
2913 uncommitted changes.
2910
2914
2911 Return 0 on success.
2915 Return 0 on success.
2912 """
2916 """
2913 q = repo.mq
2917 q = repo.mq
2914 mergeq = None
2918 mergeq = None
2915
2919
2916 opts = fixkeepchangesopts(ui, opts)
2920 opts = fixkeepchangesopts(ui, opts)
2917 if opts.get('merge'):
2921 if opts.get('merge'):
2918 if opts.get('name'):
2922 if opts.get('name'):
2919 newpath = repo.vfs.join(opts.get('name'))
2923 newpath = repo.vfs.join(opts.get('name'))
2920 else:
2924 else:
2921 newpath, i = lastsavename(q.path)
2925 newpath, i = lastsavename(q.path)
2922 if not newpath:
2926 if not newpath:
2923 ui.warn(_("no saved queues found, please use -n\n"))
2927 ui.warn(_("no saved queues found, please use -n\n"))
2924 return 1
2928 return 1
2925 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2929 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2926 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2930 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2927 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2931 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2928 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2932 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2929 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2933 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2930 keepchanges=opts.get('keep_changes'))
2934 keepchanges=opts.get('keep_changes'))
2931 return ret
2935 return ret
2932
2936
2933 @command("^qpop",
2937 @command("^qpop",
2934 [('a', 'all', None, _('pop all patches')),
2938 [('a', 'all', None, _('pop all patches')),
2935 ('n', 'name', '',
2939 ('n', 'name', '',
2936 _('queue name to pop (DEPRECATED)'), _('NAME')),
2940 _('queue name to pop (DEPRECATED)'), _('NAME')),
2937 ('', 'keep-changes', None,
2941 ('', 'keep-changes', None,
2938 _('tolerate non-conflicting local changes')),
2942 _('tolerate non-conflicting local changes')),
2939 ('f', 'force', None, _('forget any local changes to patched files')),
2943 ('f', 'force', None, _('forget any local changes to patched files')),
2940 ('', 'no-backup', None, _('do not save backup copies of files'))],
2944 ('', 'no-backup', None, _('do not save backup copies of files'))],
2941 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2945 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2942 def pop(ui, repo, patch=None, **opts):
2946 def pop(ui, repo, patch=None, **opts):
2943 """pop the current patch off the stack
2947 """pop the current patch off the stack
2944
2948
2945 Without argument, pops off the top of the patch stack. If given a
2949 Without argument, pops off the top of the patch stack. If given a
2946 patch name, keeps popping off patches until the named patch is at
2950 patch name, keeps popping off patches until the named patch is at
2947 the top of the stack.
2951 the top of the stack.
2948
2952
2949 By default, abort if the working directory contains uncommitted
2953 By default, abort if the working directory contains uncommitted
2950 changes. With --keep-changes, abort only if the uncommitted files
2954 changes. With --keep-changes, abort only if the uncommitted files
2951 overlap with patched files. With -f/--force, backup and discard
2955 overlap with patched files. With -f/--force, backup and discard
2952 changes made to such files.
2956 changes made to such files.
2953
2957
2954 Return 0 on success.
2958 Return 0 on success.
2955 """
2959 """
2956 opts = fixkeepchangesopts(ui, opts)
2960 opts = fixkeepchangesopts(ui, opts)
2957 localupdate = True
2961 localupdate = True
2958 if opts.get('name'):
2962 if opts.get('name'):
2959 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
2963 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
2960 ui.warn(_('using patch queue: %s\n') % q.path)
2964 ui.warn(_('using patch queue: %s\n') % q.path)
2961 localupdate = False
2965 localupdate = False
2962 else:
2966 else:
2963 q = repo.mq
2967 q = repo.mq
2964 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2968 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2965 all=opts.get('all'), nobackup=opts.get('no_backup'),
2969 all=opts.get('all'), nobackup=opts.get('no_backup'),
2966 keepchanges=opts.get('keep_changes'))
2970 keepchanges=opts.get('keep_changes'))
2967 q.savedirty()
2971 q.savedirty()
2968 return ret
2972 return ret
2969
2973
2970 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2974 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2971 def rename(ui, repo, patch, name=None, **opts):
2975 def rename(ui, repo, patch, name=None, **opts):
2972 """rename a patch
2976 """rename a patch
2973
2977
2974 With one argument, renames the current patch to PATCH1.
2978 With one argument, renames the current patch to PATCH1.
2975 With two arguments, renames PATCH1 to PATCH2.
2979 With two arguments, renames PATCH1 to PATCH2.
2976
2980
2977 Returns 0 on success."""
2981 Returns 0 on success."""
2978 q = repo.mq
2982 q = repo.mq
2979 if not name:
2983 if not name:
2980 name = patch
2984 name = patch
2981 patch = None
2985 patch = None
2982
2986
2983 if patch:
2987 if patch:
2984 patch = q.lookup(patch)
2988 patch = q.lookup(patch)
2985 else:
2989 else:
2986 if not q.applied:
2990 if not q.applied:
2987 ui.write(_('no patches applied\n'))
2991 ui.write(_('no patches applied\n'))
2988 return
2992 return
2989 patch = q.lookup('qtip')
2993 patch = q.lookup('qtip')
2990 absdest = q.join(name)
2994 absdest = q.join(name)
2991 if os.path.isdir(absdest):
2995 if os.path.isdir(absdest):
2992 name = normname(os.path.join(name, os.path.basename(patch)))
2996 name = normname(os.path.join(name, os.path.basename(patch)))
2993 absdest = q.join(name)
2997 absdest = q.join(name)
2994 q.checkpatchname(name)
2998 q.checkpatchname(name)
2995
2999
2996 ui.note(_('renaming %s to %s\n') % (patch, name))
3000 ui.note(_('renaming %s to %s\n') % (patch, name))
2997 i = q.findseries(patch)
3001 i = q.findseries(patch)
2998 guards = q.guard_re.findall(q.fullseries[i])
3002 guards = q.guard_re.findall(q.fullseries[i])
2999 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3003 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3000 q.parseseries()
3004 q.parseseries()
3001 q.seriesdirty = True
3005 q.seriesdirty = True
3002
3006
3003 info = q.isapplied(patch)
3007 info = q.isapplied(patch)
3004 if info:
3008 if info:
3005 q.applied[info[0]] = statusentry(info[1], name)
3009 q.applied[info[0]] = statusentry(info[1], name)
3006 q.applieddirty = True
3010 q.applieddirty = True
3007
3011
3008 destdir = os.path.dirname(absdest)
3012 destdir = os.path.dirname(absdest)
3009 if not os.path.isdir(destdir):
3013 if not os.path.isdir(destdir):
3010 os.makedirs(destdir)
3014 os.makedirs(destdir)
3011 util.rename(q.join(patch), absdest)
3015 util.rename(q.join(patch), absdest)
3012 r = q.qrepo()
3016 r = q.qrepo()
3013 if r and patch in r.dirstate:
3017 if r and patch in r.dirstate:
3014 wctx = r[None]
3018 wctx = r[None]
3015 with r.wlock():
3019 with r.wlock():
3016 if r.dirstate[patch] == 'a':
3020 if r.dirstate[patch] == 'a':
3017 r.dirstate.drop(patch)
3021 r.dirstate.drop(patch)
3018 r.dirstate.add(name)
3022 r.dirstate.add(name)
3019 else:
3023 else:
3020 wctx.copy(patch, name)
3024 wctx.copy(patch, name)
3021 wctx.forget([patch])
3025 wctx.forget([patch])
3022
3026
3023 q.savedirty()
3027 q.savedirty()
3024
3028
3025 @command("qrestore",
3029 @command("qrestore",
3026 [('d', 'delete', None, _('delete save entry')),
3030 [('d', 'delete', None, _('delete save entry')),
3027 ('u', 'update', None, _('update queue working directory'))],
3031 ('u', 'update', None, _('update queue working directory'))],
3028 _('hg qrestore [-d] [-u] REV'))
3032 _('hg qrestore [-d] [-u] REV'))
3029 def restore(ui, repo, rev, **opts):
3033 def restore(ui, repo, rev, **opts):
3030 """restore the queue state saved by a revision (DEPRECATED)
3034 """restore the queue state saved by a revision (DEPRECATED)
3031
3035
3032 This command is deprecated, use :hg:`rebase` instead."""
3036 This command is deprecated, use :hg:`rebase` instead."""
3033 rev = repo.lookup(rev)
3037 rev = repo.lookup(rev)
3034 q = repo.mq
3038 q = repo.mq
3035 q.restore(repo, rev, delete=opts.get('delete'),
3039 q.restore(repo, rev, delete=opts.get('delete'),
3036 qupdate=opts.get('update'))
3040 qupdate=opts.get('update'))
3037 q.savedirty()
3041 q.savedirty()
3038 return 0
3042 return 0
3039
3043
3040 @command("qsave",
3044 @command("qsave",
3041 [('c', 'copy', None, _('copy patch directory')),
3045 [('c', 'copy', None, _('copy patch directory')),
3042 ('n', 'name', '',
3046 ('n', 'name', '',
3043 _('copy directory name'), _('NAME')),
3047 _('copy directory name'), _('NAME')),
3044 ('e', 'empty', None, _('clear queue status file')),
3048 ('e', 'empty', None, _('clear queue status file')),
3045 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3049 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3046 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3050 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3047 def save(ui, repo, **opts):
3051 def save(ui, repo, **opts):
3048 """save current queue state (DEPRECATED)
3052 """save current queue state (DEPRECATED)
3049
3053
3050 This command is deprecated, use :hg:`rebase` instead."""
3054 This command is deprecated, use :hg:`rebase` instead."""
3051 q = repo.mq
3055 q = repo.mq
3052 message = cmdutil.logmessage(ui, opts)
3056 message = cmdutil.logmessage(ui, opts)
3053 ret = q.save(repo, msg=message)
3057 ret = q.save(repo, msg=message)
3054 if ret:
3058 if ret:
3055 return ret
3059 return ret
3056 q.savedirty() # save to .hg/patches before copying
3060 q.savedirty() # save to .hg/patches before copying
3057 if opts.get('copy'):
3061 if opts.get('copy'):
3058 path = q.path
3062 path = q.path
3059 if opts.get('name'):
3063 if opts.get('name'):
3060 newpath = os.path.join(q.basepath, opts.get('name'))
3064 newpath = os.path.join(q.basepath, opts.get('name'))
3061 if os.path.exists(newpath):
3065 if os.path.exists(newpath):
3062 if not os.path.isdir(newpath):
3066 if not os.path.isdir(newpath):
3063 raise error.Abort(_('destination %s exists and is not '
3067 raise error.Abort(_('destination %s exists and is not '
3064 'a directory') % newpath)
3068 'a directory') % newpath)
3065 if not opts.get('force'):
3069 if not opts.get('force'):
3066 raise error.Abort(_('destination %s exists, '
3070 raise error.Abort(_('destination %s exists, '
3067 'use -f to force') % newpath)
3071 'use -f to force') % newpath)
3068 else:
3072 else:
3069 newpath = savename(path)
3073 newpath = savename(path)
3070 ui.warn(_("copy %s to %s\n") % (path, newpath))
3074 ui.warn(_("copy %s to %s\n") % (path, newpath))
3071 util.copyfiles(path, newpath)
3075 util.copyfiles(path, newpath)
3072 if opts.get('empty'):
3076 if opts.get('empty'):
3073 del q.applied[:]
3077 del q.applied[:]
3074 q.applieddirty = True
3078 q.applieddirty = True
3075 q.savedirty()
3079 q.savedirty()
3076 return 0
3080 return 0
3077
3081
3078
3082
3079 @command("qselect",
3083 @command("qselect",
3080 [('n', 'none', None, _('disable all guards')),
3084 [('n', 'none', None, _('disable all guards')),
3081 ('s', 'series', None, _('list all guards in series file')),
3085 ('s', 'series', None, _('list all guards in series file')),
3082 ('', 'pop', None, _('pop to before first guarded applied patch')),
3086 ('', 'pop', None, _('pop to before first guarded applied patch')),
3083 ('', 'reapply', None, _('pop, then reapply patches'))],
3087 ('', 'reapply', None, _('pop, then reapply patches'))],
3084 _('hg qselect [OPTION]... [GUARD]...'))
3088 _('hg qselect [OPTION]... [GUARD]...'))
3085 def select(ui, repo, *args, **opts):
3089 def select(ui, repo, *args, **opts):
3086 '''set or print guarded patches to push
3090 '''set or print guarded patches to push
3087
3091
3088 Use the :hg:`qguard` command to set or print guards on patch, then use
3092 Use the :hg:`qguard` command to set or print guards on patch, then use
3089 qselect to tell mq which guards to use. A patch will be pushed if
3093 qselect to tell mq which guards to use. A patch will be pushed if
3090 it has no guards or any positive guards match the currently
3094 it has no guards or any positive guards match the currently
3091 selected guard, but will not be pushed if any negative guards
3095 selected guard, but will not be pushed if any negative guards
3092 match the current guard. For example::
3096 match the current guard. For example::
3093
3097
3094 qguard foo.patch -- -stable (negative guard)
3098 qguard foo.patch -- -stable (negative guard)
3095 qguard bar.patch +stable (positive guard)
3099 qguard bar.patch +stable (positive guard)
3096 qselect stable
3100 qselect stable
3097
3101
3098 This activates the "stable" guard. mq will skip foo.patch (because
3102 This activates the "stable" guard. mq will skip foo.patch (because
3099 it has a negative match) but push bar.patch (because it has a
3103 it has a negative match) but push bar.patch (because it has a
3100 positive match).
3104 positive match).
3101
3105
3102 With no arguments, prints the currently active guards.
3106 With no arguments, prints the currently active guards.
3103 With one argument, sets the active guard.
3107 With one argument, sets the active guard.
3104
3108
3105 Use -n/--none to deactivate guards (no other arguments needed).
3109 Use -n/--none to deactivate guards (no other arguments needed).
3106 When no guards are active, patches with positive guards are
3110 When no guards are active, patches with positive guards are
3107 skipped and patches with negative guards are pushed.
3111 skipped and patches with negative guards are pushed.
3108
3112
3109 qselect can change the guards on applied patches. It does not pop
3113 qselect can change the guards on applied patches. It does not pop
3110 guarded patches by default. Use --pop to pop back to the last
3114 guarded patches by default. Use --pop to pop back to the last
3111 applied patch that is not guarded. Use --reapply (which implies
3115 applied patch that is not guarded. Use --reapply (which implies
3112 --pop) to push back to the current patch afterwards, but skip
3116 --pop) to push back to the current patch afterwards, but skip
3113 guarded patches.
3117 guarded patches.
3114
3118
3115 Use -s/--series to print a list of all guards in the series file
3119 Use -s/--series to print a list of all guards in the series file
3116 (no other arguments needed). Use -v for more information.
3120 (no other arguments needed). Use -v for more information.
3117
3121
3118 Returns 0 on success.'''
3122 Returns 0 on success.'''
3119
3123
3120 q = repo.mq
3124 q = repo.mq
3121 guards = q.active()
3125 guards = q.active()
3122 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3126 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3123 if args or opts.get('none'):
3127 if args or opts.get('none'):
3124 old_unapplied = q.unapplied(repo)
3128 old_unapplied = q.unapplied(repo)
3125 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3129 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3126 q.setactive(args)
3130 q.setactive(args)
3127 q.savedirty()
3131 q.savedirty()
3128 if not args:
3132 if not args:
3129 ui.status(_('guards deactivated\n'))
3133 ui.status(_('guards deactivated\n'))
3130 if not opts.get('pop') and not opts.get('reapply'):
3134 if not opts.get('pop') and not opts.get('reapply'):
3131 unapplied = q.unapplied(repo)
3135 unapplied = q.unapplied(repo)
3132 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3136 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3133 if len(unapplied) != len(old_unapplied):
3137 if len(unapplied) != len(old_unapplied):
3134 ui.status(_('number of unguarded, unapplied patches has '
3138 ui.status(_('number of unguarded, unapplied patches has '
3135 'changed from %d to %d\n') %
3139 'changed from %d to %d\n') %
3136 (len(old_unapplied), len(unapplied)))
3140 (len(old_unapplied), len(unapplied)))
3137 if len(guarded) != len(old_guarded):
3141 if len(guarded) != len(old_guarded):
3138 ui.status(_('number of guarded, applied patches has changed '
3142 ui.status(_('number of guarded, applied patches has changed '
3139 'from %d to %d\n') %
3143 'from %d to %d\n') %
3140 (len(old_guarded), len(guarded)))
3144 (len(old_guarded), len(guarded)))
3141 elif opts.get('series'):
3145 elif opts.get('series'):
3142 guards = {}
3146 guards = {}
3143 noguards = 0
3147 noguards = 0
3144 for gs in q.seriesguards:
3148 for gs in q.seriesguards:
3145 if not gs:
3149 if not gs:
3146 noguards += 1
3150 noguards += 1
3147 for g in gs:
3151 for g in gs:
3148 guards.setdefault(g, 0)
3152 guards.setdefault(g, 0)
3149 guards[g] += 1
3153 guards[g] += 1
3150 if ui.verbose:
3154 if ui.verbose:
3151 guards['NONE'] = noguards
3155 guards['NONE'] = noguards
3152 guards = guards.items()
3156 guards = guards.items()
3153 guards.sort(key=lambda x: x[0][1:])
3157 guards.sort(key=lambda x: x[0][1:])
3154 if guards:
3158 if guards:
3155 ui.note(_('guards in series file:\n'))
3159 ui.note(_('guards in series file:\n'))
3156 for guard, count in guards:
3160 for guard, count in guards:
3157 ui.note('%2d ' % count)
3161 ui.note('%2d ' % count)
3158 ui.write(guard, '\n')
3162 ui.write(guard, '\n')
3159 else:
3163 else:
3160 ui.note(_('no guards in series file\n'))
3164 ui.note(_('no guards in series file\n'))
3161 else:
3165 else:
3162 if guards:
3166 if guards:
3163 ui.note(_('active guards:\n'))
3167 ui.note(_('active guards:\n'))
3164 for g in guards:
3168 for g in guards:
3165 ui.write(g, '\n')
3169 ui.write(g, '\n')
3166 else:
3170 else:
3167 ui.write(_('no active guards\n'))
3171 ui.write(_('no active guards\n'))
3168 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3172 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3169 popped = False
3173 popped = False
3170 if opts.get('pop') or opts.get('reapply'):
3174 if opts.get('pop') or opts.get('reapply'):
3171 for i in xrange(len(q.applied)):
3175 for i in xrange(len(q.applied)):
3172 if not pushable(i):
3176 if not pushable(i):
3173 ui.status(_('popping guarded patches\n'))
3177 ui.status(_('popping guarded patches\n'))
3174 popped = True
3178 popped = True
3175 if i == 0:
3179 if i == 0:
3176 q.pop(repo, all=True)
3180 q.pop(repo, all=True)
3177 else:
3181 else:
3178 q.pop(repo, q.applied[i - 1].name)
3182 q.pop(repo, q.applied[i - 1].name)
3179 break
3183 break
3180 if popped:
3184 if popped:
3181 try:
3185 try:
3182 if reapply:
3186 if reapply:
3183 ui.status(_('reapplying unguarded patches\n'))
3187 ui.status(_('reapplying unguarded patches\n'))
3184 q.push(repo, reapply)
3188 q.push(repo, reapply)
3185 finally:
3189 finally:
3186 q.savedirty()
3190 q.savedirty()
3187
3191
3188 @command("qfinish",
3192 @command("qfinish",
3189 [('a', 'applied', None, _('finish all applied changesets'))],
3193 [('a', 'applied', None, _('finish all applied changesets'))],
3190 _('hg qfinish [-a] [REV]...'))
3194 _('hg qfinish [-a] [REV]...'))
3191 def finish(ui, repo, *revrange, **opts):
3195 def finish(ui, repo, *revrange, **opts):
3192 """move applied patches into repository history
3196 """move applied patches into repository history
3193
3197
3194 Finishes the specified revisions (corresponding to applied
3198 Finishes the specified revisions (corresponding to applied
3195 patches) by moving them out of mq control into regular repository
3199 patches) by moving them out of mq control into regular repository
3196 history.
3200 history.
3197
3201
3198 Accepts a revision range or the -a/--applied option. If --applied
3202 Accepts a revision range or the -a/--applied option. If --applied
3199 is specified, all applied mq revisions are removed from mq
3203 is specified, all applied mq revisions are removed from mq
3200 control. Otherwise, the given revisions must be at the base of the
3204 control. Otherwise, the given revisions must be at the base of the
3201 stack of applied patches.
3205 stack of applied patches.
3202
3206
3203 This can be especially useful if your changes have been applied to
3207 This can be especially useful if your changes have been applied to
3204 an upstream repository, or if you are about to push your changes
3208 an upstream repository, or if you are about to push your changes
3205 to upstream.
3209 to upstream.
3206
3210
3207 Returns 0 on success.
3211 Returns 0 on success.
3208 """
3212 """
3209 if not opts.get('applied') and not revrange:
3213 if not opts.get('applied') and not revrange:
3210 raise error.Abort(_('no revisions specified'))
3214 raise error.Abort(_('no revisions specified'))
3211 elif opts.get('applied'):
3215 elif opts.get('applied'):
3212 revrange = ('qbase::qtip',) + revrange
3216 revrange = ('qbase::qtip',) + revrange
3213
3217
3214 q = repo.mq
3218 q = repo.mq
3215 if not q.applied:
3219 if not q.applied:
3216 ui.status(_('no patches applied\n'))
3220 ui.status(_('no patches applied\n'))
3217 return 0
3221 return 0
3218
3222
3219 revs = scmutil.revrange(repo, revrange)
3223 revs = scmutil.revrange(repo, revrange)
3220 if repo['.'].rev() in revs and repo[None].files():
3224 if repo['.'].rev() in revs and repo[None].files():
3221 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3225 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3222 # queue.finish may changes phases but leave the responsibility to lock the
3226 # queue.finish may changes phases but leave the responsibility to lock the
3223 # repo to the caller to avoid deadlock with wlock. This command code is
3227 # repo to the caller to avoid deadlock with wlock. This command code is
3224 # responsibility for this locking.
3228 # responsibility for this locking.
3225 with repo.lock():
3229 with repo.lock():
3226 q.finish(repo, revs)
3230 q.finish(repo, revs)
3227 q.savedirty()
3231 q.savedirty()
3228 return 0
3232 return 0
3229
3233
3230 @command("qqueue",
3234 @command("qqueue",
3231 [('l', 'list', False, _('list all available queues')),
3235 [('l', 'list', False, _('list all available queues')),
3232 ('', 'active', False, _('print name of active queue')),
3236 ('', 'active', False, _('print name of active queue')),
3233 ('c', 'create', False, _('create new queue')),
3237 ('c', 'create', False, _('create new queue')),
3234 ('', 'rename', False, _('rename active queue')),
3238 ('', 'rename', False, _('rename active queue')),
3235 ('', 'delete', False, _('delete reference to queue')),
3239 ('', 'delete', False, _('delete reference to queue')),
3236 ('', 'purge', False, _('delete queue, and remove patch dir')),
3240 ('', 'purge', False, _('delete queue, and remove patch dir')),
3237 ],
3241 ],
3238 _('[OPTION] [QUEUE]'))
3242 _('[OPTION] [QUEUE]'))
3239 def qqueue(ui, repo, name=None, **opts):
3243 def qqueue(ui, repo, name=None, **opts):
3240 '''manage multiple patch queues
3244 '''manage multiple patch queues
3241
3245
3242 Supports switching between different patch queues, as well as creating
3246 Supports switching between different patch queues, as well as creating
3243 new patch queues and deleting existing ones.
3247 new patch queues and deleting existing ones.
3244
3248
3245 Omitting a queue name or specifying -l/--list will show you the registered
3249 Omitting a queue name or specifying -l/--list will show you the registered
3246 queues - by default the "normal" patches queue is registered. The currently
3250 queues - by default the "normal" patches queue is registered. The currently
3247 active queue will be marked with "(active)". Specifying --active will print
3251 active queue will be marked with "(active)". Specifying --active will print
3248 only the name of the active queue.
3252 only the name of the active queue.
3249
3253
3250 To create a new queue, use -c/--create. The queue is automatically made
3254 To create a new queue, use -c/--create. The queue is automatically made
3251 active, except in the case where there are applied patches from the
3255 active, except in the case where there are applied patches from the
3252 currently active queue in the repository. Then the queue will only be
3256 currently active queue in the repository. Then the queue will only be
3253 created and switching will fail.
3257 created and switching will fail.
3254
3258
3255 To delete an existing queue, use --delete. You cannot delete the currently
3259 To delete an existing queue, use --delete. You cannot delete the currently
3256 active queue.
3260 active queue.
3257
3261
3258 Returns 0 on success.
3262 Returns 0 on success.
3259 '''
3263 '''
3260 q = repo.mq
3264 q = repo.mq
3261 _defaultqueue = 'patches'
3265 _defaultqueue = 'patches'
3262 _allqueues = 'patches.queues'
3266 _allqueues = 'patches.queues'
3263 _activequeue = 'patches.queue'
3267 _activequeue = 'patches.queue'
3264
3268
3265 def _getcurrent():
3269 def _getcurrent():
3266 cur = os.path.basename(q.path)
3270 cur = os.path.basename(q.path)
3267 if cur.startswith('patches-'):
3271 if cur.startswith('patches-'):
3268 cur = cur[8:]
3272 cur = cur[8:]
3269 return cur
3273 return cur
3270
3274
3271 def _noqueues():
3275 def _noqueues():
3272 try:
3276 try:
3273 fh = repo.vfs(_allqueues, 'r')
3277 fh = repo.vfs(_allqueues, 'r')
3274 fh.close()
3278 fh.close()
3275 except IOError:
3279 except IOError:
3276 return True
3280 return True
3277
3281
3278 return False
3282 return False
3279
3283
3280 def _getqueues():
3284 def _getqueues():
3281 current = _getcurrent()
3285 current = _getcurrent()
3282
3286
3283 try:
3287 try:
3284 fh = repo.vfs(_allqueues, 'r')
3288 fh = repo.vfs(_allqueues, 'r')
3285 queues = [queue.strip() for queue in fh if queue.strip()]
3289 queues = [queue.strip() for queue in fh if queue.strip()]
3286 fh.close()
3290 fh.close()
3287 if current not in queues:
3291 if current not in queues:
3288 queues.append(current)
3292 queues.append(current)
3289 except IOError:
3293 except IOError:
3290 queues = [_defaultqueue]
3294 queues = [_defaultqueue]
3291
3295
3292 return sorted(queues)
3296 return sorted(queues)
3293
3297
3294 def _setactive(name):
3298 def _setactive(name):
3295 if q.applied:
3299 if q.applied:
3296 raise error.Abort(_('new queue created, but cannot make active '
3300 raise error.Abort(_('new queue created, but cannot make active '
3297 'as patches are applied'))
3301 'as patches are applied'))
3298 _setactivenocheck(name)
3302 _setactivenocheck(name)
3299
3303
3300 def _setactivenocheck(name):
3304 def _setactivenocheck(name):
3301 fh = repo.vfs(_activequeue, 'w')
3305 fh = repo.vfs(_activequeue, 'w')
3302 if name != 'patches':
3306 if name != 'patches':
3303 fh.write(name)
3307 fh.write(name)
3304 fh.close()
3308 fh.close()
3305
3309
3306 def _addqueue(name):
3310 def _addqueue(name):
3307 fh = repo.vfs(_allqueues, 'a')
3311 fh = repo.vfs(_allqueues, 'a')
3308 fh.write('%s\n' % (name,))
3312 fh.write('%s\n' % (name,))
3309 fh.close()
3313 fh.close()
3310
3314
3311 def _queuedir(name):
3315 def _queuedir(name):
3312 if name == 'patches':
3316 if name == 'patches':
3313 return repo.vfs.join('patches')
3317 return repo.vfs.join('patches')
3314 else:
3318 else:
3315 return repo.vfs.join('patches-' + name)
3319 return repo.vfs.join('patches-' + name)
3316
3320
3317 def _validname(name):
3321 def _validname(name):
3318 for n in name:
3322 for n in name:
3319 if n in ':\\/.':
3323 if n in ':\\/.':
3320 return False
3324 return False
3321 return True
3325 return True
3322
3326
3323 def _delete(name):
3327 def _delete(name):
3324 if name not in existing:
3328 if name not in existing:
3325 raise error.Abort(_('cannot delete queue that does not exist'))
3329 raise error.Abort(_('cannot delete queue that does not exist'))
3326
3330
3327 current = _getcurrent()
3331 current = _getcurrent()
3328
3332
3329 if name == current:
3333 if name == current:
3330 raise error.Abort(_('cannot delete currently active queue'))
3334 raise error.Abort(_('cannot delete currently active queue'))
3331
3335
3332 fh = repo.vfs('patches.queues.new', 'w')
3336 fh = repo.vfs('patches.queues.new', 'w')
3333 for queue in existing:
3337 for queue in existing:
3334 if queue == name:
3338 if queue == name:
3335 continue
3339 continue
3336 fh.write('%s\n' % (queue,))
3340 fh.write('%s\n' % (queue,))
3337 fh.close()
3341 fh.close()
3338 repo.vfs.rename('patches.queues.new', _allqueues)
3342 repo.vfs.rename('patches.queues.new', _allqueues)
3339
3343
3340 if not name or opts.get('list') or opts.get('active'):
3344 if not name or opts.get('list') or opts.get('active'):
3341 current = _getcurrent()
3345 current = _getcurrent()
3342 if opts.get('active'):
3346 if opts.get('active'):
3343 ui.write('%s\n' % (current,))
3347 ui.write('%s\n' % (current,))
3344 return
3348 return
3345 for queue in _getqueues():
3349 for queue in _getqueues():
3346 ui.write('%s' % (queue,))
3350 ui.write('%s' % (queue,))
3347 if queue == current and not ui.quiet:
3351 if queue == current and not ui.quiet:
3348 ui.write(_(' (active)\n'))
3352 ui.write(_(' (active)\n'))
3349 else:
3353 else:
3350 ui.write('\n')
3354 ui.write('\n')
3351 return
3355 return
3352
3356
3353 if not _validname(name):
3357 if not _validname(name):
3354 raise error.Abort(
3358 raise error.Abort(
3355 _('invalid queue name, may not contain the characters ":\\/."'))
3359 _('invalid queue name, may not contain the characters ":\\/."'))
3356
3360
3357 with repo.wlock():
3361 with repo.wlock():
3358 existing = _getqueues()
3362 existing = _getqueues()
3359
3363
3360 if opts.get('create'):
3364 if opts.get('create'):
3361 if name in existing:
3365 if name in existing:
3362 raise error.Abort(_('queue "%s" already exists') % name)
3366 raise error.Abort(_('queue "%s" already exists') % name)
3363 if _noqueues():
3367 if _noqueues():
3364 _addqueue(_defaultqueue)
3368 _addqueue(_defaultqueue)
3365 _addqueue(name)
3369 _addqueue(name)
3366 _setactive(name)
3370 _setactive(name)
3367 elif opts.get('rename'):
3371 elif opts.get('rename'):
3368 current = _getcurrent()
3372 current = _getcurrent()
3369 if name == current:
3373 if name == current:
3370 raise error.Abort(_('can\'t rename "%s" to its current name')
3374 raise error.Abort(_('can\'t rename "%s" to its current name')
3371 % name)
3375 % name)
3372 if name in existing:
3376 if name in existing:
3373 raise error.Abort(_('queue "%s" already exists') % name)
3377 raise error.Abort(_('queue "%s" already exists') % name)
3374
3378
3375 olddir = _queuedir(current)
3379 olddir = _queuedir(current)
3376 newdir = _queuedir(name)
3380 newdir = _queuedir(name)
3377
3381
3378 if os.path.exists(newdir):
3382 if os.path.exists(newdir):
3379 raise error.Abort(_('non-queue directory "%s" already exists') %
3383 raise error.Abort(_('non-queue directory "%s" already exists') %
3380 newdir)
3384 newdir)
3381
3385
3382 fh = repo.vfs('patches.queues.new', 'w')
3386 fh = repo.vfs('patches.queues.new', 'w')
3383 for queue in existing:
3387 for queue in existing:
3384 if queue == current:
3388 if queue == current:
3385 fh.write('%s\n' % (name,))
3389 fh.write('%s\n' % (name,))
3386 if os.path.exists(olddir):
3390 if os.path.exists(olddir):
3387 util.rename(olddir, newdir)
3391 util.rename(olddir, newdir)
3388 else:
3392 else:
3389 fh.write('%s\n' % (queue,))
3393 fh.write('%s\n' % (queue,))
3390 fh.close()
3394 fh.close()
3391 repo.vfs.rename('patches.queues.new', _allqueues)
3395 repo.vfs.rename('patches.queues.new', _allqueues)
3392 _setactivenocheck(name)
3396 _setactivenocheck(name)
3393 elif opts.get('delete'):
3397 elif opts.get('delete'):
3394 _delete(name)
3398 _delete(name)
3395 elif opts.get('purge'):
3399 elif opts.get('purge'):
3396 if name in existing:
3400 if name in existing:
3397 _delete(name)
3401 _delete(name)
3398 qdir = _queuedir(name)
3402 qdir = _queuedir(name)
3399 if os.path.exists(qdir):
3403 if os.path.exists(qdir):
3400 shutil.rmtree(qdir)
3404 shutil.rmtree(qdir)
3401 else:
3405 else:
3402 if name not in existing:
3406 if name not in existing:
3403 raise error.Abort(_('use --create to create a new queue'))
3407 raise error.Abort(_('use --create to create a new queue'))
3404 _setactive(name)
3408 _setactive(name)
3405
3409
3406 def mqphasedefaults(repo, roots):
3410 def mqphasedefaults(repo, roots):
3407 """callback used to set mq changeset as secret when no phase data exists"""
3411 """callback used to set mq changeset as secret when no phase data exists"""
3408 if repo.mq.applied:
3412 if repo.mq.applied:
3409 if repo.ui.configbool('mq', 'secret', False):
3413 if repo.ui.configbool('mq', 'secret', False):
3410 mqphase = phases.secret
3414 mqphase = phases.secret
3411 else:
3415 else:
3412 mqphase = phases.draft
3416 mqphase = phases.draft
3413 qbase = repo[repo.mq.applied[0].node]
3417 qbase = repo[repo.mq.applied[0].node]
3414 roots[mqphase].add(qbase.node())
3418 roots[mqphase].add(qbase.node())
3415 return roots
3419 return roots
3416
3420
3417 def reposetup(ui, repo):
3421 def reposetup(ui, repo):
3418 class mqrepo(repo.__class__):
3422 class mqrepo(repo.__class__):
3419 @localrepo.unfilteredpropertycache
3423 @localrepo.unfilteredpropertycache
3420 def mq(self):
3424 def mq(self):
3421 return queue(self.ui, self.baseui, self.path)
3425 return queue(self.ui, self.baseui, self.path)
3422
3426
3423 def invalidateall(self):
3427 def invalidateall(self):
3424 super(mqrepo, self).invalidateall()
3428 super(mqrepo, self).invalidateall()
3425 if localrepo.hasunfilteredcache(self, 'mq'):
3429 if localrepo.hasunfilteredcache(self, 'mq'):
3426 # recreate mq in case queue path was changed
3430 # recreate mq in case queue path was changed
3427 delattr(self.unfiltered(), 'mq')
3431 delattr(self.unfiltered(), 'mq')
3428
3432
3429 def abortifwdirpatched(self, errmsg, force=False):
3433 def abortifwdirpatched(self, errmsg, force=False):
3430 if self.mq.applied and self.mq.checkapplied and not force:
3434 if self.mq.applied and self.mq.checkapplied and not force:
3431 parents = self.dirstate.parents()
3435 parents = self.dirstate.parents()
3432 patches = [s.node for s in self.mq.applied]
3436 patches = [s.node for s in self.mq.applied]
3433 if parents[0] in patches or parents[1] in patches:
3437 if parents[0] in patches or parents[1] in patches:
3434 raise error.Abort(errmsg)
3438 raise error.Abort(errmsg)
3435
3439
3436 def commit(self, text="", user=None, date=None, match=None,
3440 def commit(self, text="", user=None, date=None, match=None,
3437 force=False, editor=False, extra=None):
3441 force=False, editor=False, extra=None):
3438 if extra is None:
3442 if extra is None:
3439 extra = {}
3443 extra = {}
3440 self.abortifwdirpatched(
3444 self.abortifwdirpatched(
3441 _('cannot commit over an applied mq patch'),
3445 _('cannot commit over an applied mq patch'),
3442 force)
3446 force)
3443
3447
3444 return super(mqrepo, self).commit(text, user, date, match, force,
3448 return super(mqrepo, self).commit(text, user, date, match, force,
3445 editor, extra)
3449 editor, extra)
3446
3450
3447 def checkpush(self, pushop):
3451 def checkpush(self, pushop):
3448 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3452 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3449 outapplied = [e.node for e in self.mq.applied]
3453 outapplied = [e.node for e in self.mq.applied]
3450 if pushop.revs:
3454 if pushop.revs:
3451 # Assume applied patches have no non-patch descendants and
3455 # Assume applied patches have no non-patch descendants and
3452 # are not on remote already. Filtering any changeset not
3456 # are not on remote already. Filtering any changeset not
3453 # pushed.
3457 # pushed.
3454 heads = set(pushop.revs)
3458 heads = set(pushop.revs)
3455 for node in reversed(outapplied):
3459 for node in reversed(outapplied):
3456 if node in heads:
3460 if node in heads:
3457 break
3461 break
3458 else:
3462 else:
3459 outapplied.pop()
3463 outapplied.pop()
3460 # looking for pushed and shared changeset
3464 # looking for pushed and shared changeset
3461 for node in outapplied:
3465 for node in outapplied:
3462 if self[node].phase() < phases.secret:
3466 if self[node].phase() < phases.secret:
3463 raise error.Abort(_('source has mq patches applied'))
3467 raise error.Abort(_('source has mq patches applied'))
3464 # no non-secret patches pushed
3468 # no non-secret patches pushed
3465 super(mqrepo, self).checkpush(pushop)
3469 super(mqrepo, self).checkpush(pushop)
3466
3470
3467 def _findtags(self):
3471 def _findtags(self):
3468 '''augment tags from base class with patch tags'''
3472 '''augment tags from base class with patch tags'''
3469 result = super(mqrepo, self)._findtags()
3473 result = super(mqrepo, self)._findtags()
3470
3474
3471 q = self.mq
3475 q = self.mq
3472 if not q.applied:
3476 if not q.applied:
3473 return result
3477 return result
3474
3478
3475 mqtags = [(patch.node, patch.name) for patch in q.applied]
3479 mqtags = [(patch.node, patch.name) for patch in q.applied]
3476
3480
3477 try:
3481 try:
3478 # for now ignore filtering business
3482 # for now ignore filtering business
3479 self.unfiltered().changelog.rev(mqtags[-1][0])
3483 self.unfiltered().changelog.rev(mqtags[-1][0])
3480 except error.LookupError:
3484 except error.LookupError:
3481 self.ui.warn(_('mq status file refers to unknown node %s\n')
3485 self.ui.warn(_('mq status file refers to unknown node %s\n')
3482 % short(mqtags[-1][0]))
3486 % short(mqtags[-1][0]))
3483 return result
3487 return result
3484
3488
3485 # do not add fake tags for filtered revisions
3489 # do not add fake tags for filtered revisions
3486 included = self.changelog.hasnode
3490 included = self.changelog.hasnode
3487 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3491 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3488 if not mqtags:
3492 if not mqtags:
3489 return result
3493 return result
3490
3494
3491 mqtags.append((mqtags[-1][0], 'qtip'))
3495 mqtags.append((mqtags[-1][0], 'qtip'))
3492 mqtags.append((mqtags[0][0], 'qbase'))
3496 mqtags.append((mqtags[0][0], 'qbase'))
3493 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3497 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3494 tags = result[0]
3498 tags = result[0]
3495 for patch in mqtags:
3499 for patch in mqtags:
3496 if patch[1] in tags:
3500 if patch[1] in tags:
3497 self.ui.warn(_('tag %s overrides mq patch of the same '
3501 self.ui.warn(_('tag %s overrides mq patch of the same '
3498 'name\n') % patch[1])
3502 'name\n') % patch[1])
3499 else:
3503 else:
3500 tags[patch[1]] = patch[0]
3504 tags[patch[1]] = patch[0]
3501
3505
3502 return result
3506 return result
3503
3507
3504 if repo.local():
3508 if repo.local():
3505 repo.__class__ = mqrepo
3509 repo.__class__ = mqrepo
3506
3510
3507 repo._phasedefaults.append(mqphasedefaults)
3511 repo._phasedefaults.append(mqphasedefaults)
3508
3512
3509 def mqimport(orig, ui, repo, *args, **kwargs):
3513 def mqimport(orig, ui, repo, *args, **kwargs):
3510 if (util.safehasattr(repo, 'abortifwdirpatched')
3514 if (util.safehasattr(repo, 'abortifwdirpatched')
3511 and not kwargs.get('no_commit', False)):
3515 and not kwargs.get('no_commit', False)):
3512 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3516 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3513 kwargs.get('force'))
3517 kwargs.get('force'))
3514 return orig(ui, repo, *args, **kwargs)
3518 return orig(ui, repo, *args, **kwargs)
3515
3519
3516 def mqinit(orig, ui, *args, **kwargs):
3520 def mqinit(orig, ui, *args, **kwargs):
3517 mq = kwargs.pop('mq', None)
3521 mq = kwargs.pop('mq', None)
3518
3522
3519 if not mq:
3523 if not mq:
3520 return orig(ui, *args, **kwargs)
3524 return orig(ui, *args, **kwargs)
3521
3525
3522 if args:
3526 if args:
3523 repopath = args[0]
3527 repopath = args[0]
3524 if not hg.islocal(repopath):
3528 if not hg.islocal(repopath):
3525 raise error.Abort(_('only a local queue repository '
3529 raise error.Abort(_('only a local queue repository '
3526 'may be initialized'))
3530 'may be initialized'))
3527 else:
3531 else:
3528 repopath = cmdutil.findrepo(pycompat.getcwd())
3532 repopath = cmdutil.findrepo(pycompat.getcwd())
3529 if not repopath:
3533 if not repopath:
3530 raise error.Abort(_('there is no Mercurial repository here '
3534 raise error.Abort(_('there is no Mercurial repository here '
3531 '(.hg not found)'))
3535 '(.hg not found)'))
3532 repo = hg.repository(ui, repopath)
3536 repo = hg.repository(ui, repopath)
3533 return qinit(ui, repo, True)
3537 return qinit(ui, repo, True)
3534
3538
3535 def mqcommand(orig, ui, repo, *args, **kwargs):
3539 def mqcommand(orig, ui, repo, *args, **kwargs):
3536 """Add --mq option to operate on patch repository instead of main"""
3540 """Add --mq option to operate on patch repository instead of main"""
3537
3541
3538 # some commands do not like getting unknown options
3542 # some commands do not like getting unknown options
3539 mq = kwargs.pop('mq', None)
3543 mq = kwargs.pop('mq', None)
3540
3544
3541 if not mq:
3545 if not mq:
3542 return orig(ui, repo, *args, **kwargs)
3546 return orig(ui, repo, *args, **kwargs)
3543
3547
3544 q = repo.mq
3548 q = repo.mq
3545 r = q.qrepo()
3549 r = q.qrepo()
3546 if not r:
3550 if not r:
3547 raise error.Abort(_('no queue repository'))
3551 raise error.Abort(_('no queue repository'))
3548 return orig(r.ui, r, *args, **kwargs)
3552 return orig(r.ui, r, *args, **kwargs)
3549
3553
3550 def summaryhook(ui, repo):
3554 def summaryhook(ui, repo):
3551 q = repo.mq
3555 q = repo.mq
3552 m = []
3556 m = []
3553 a, u = len(q.applied), len(q.unapplied(repo))
3557 a, u = len(q.applied), len(q.unapplied(repo))
3554 if a:
3558 if a:
3555 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3559 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3556 if u:
3560 if u:
3557 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3561 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3558 if m:
3562 if m:
3559 # i18n: column positioning for "hg summary"
3563 # i18n: column positioning for "hg summary"
3560 ui.write(_("mq: %s\n") % ', '.join(m))
3564 ui.write(_("mq: %s\n") % ', '.join(m))
3561 else:
3565 else:
3562 # i18n: column positioning for "hg summary"
3566 # i18n: column positioning for "hg summary"
3563 ui.note(_("mq: (empty queue)\n"))
3567 ui.note(_("mq: (empty queue)\n"))
3564
3568
3565 revsetpredicate = registrar.revsetpredicate()
3569 revsetpredicate = registrar.revsetpredicate()
3566
3570
3567 @revsetpredicate('mq()')
3571 @revsetpredicate('mq()')
3568 def revsetmq(repo, subset, x):
3572 def revsetmq(repo, subset, x):
3569 """Changesets managed by MQ.
3573 """Changesets managed by MQ.
3570 """
3574 """
3571 revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
3575 revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
3572 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3576 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3573 return smartset.baseset([r for r in subset if r in applied])
3577 return smartset.baseset([r for r in subset if r in applied])
3574
3578
3575 # tell hggettext to extract docstrings from these functions:
3579 # tell hggettext to extract docstrings from these functions:
3576 i18nfunctions = [revsetmq]
3580 i18nfunctions = [revsetmq]
3577
3581
3578 def extsetup(ui):
3582 def extsetup(ui):
3579 # Ensure mq wrappers are called first, regardless of extension load order by
3583 # Ensure mq wrappers are called first, regardless of extension load order by
3580 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3584 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3581 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3585 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3582
3586
3583 extensions.wrapcommand(commands.table, 'import', mqimport)
3587 extensions.wrapcommand(commands.table, 'import', mqimport)
3584 cmdutil.summaryhooks.add('mq', summaryhook)
3588 cmdutil.summaryhooks.add('mq', summaryhook)
3585
3589
3586 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3590 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3587 entry[1].extend(mqopt)
3591 entry[1].extend(mqopt)
3588
3592
3589 def dotable(cmdtable):
3593 def dotable(cmdtable):
3590 for cmd, entry in cmdtable.iteritems():
3594 for cmd, entry in cmdtable.iteritems():
3591 cmd = cmdutil.parsealiases(cmd)[0]
3595 cmd = cmdutil.parsealiases(cmd)[0]
3592 func = entry[0]
3596 func = entry[0]
3593 if func.norepo:
3597 if func.norepo:
3594 continue
3598 continue
3595 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3599 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3596 entry[1].extend(mqopt)
3600 entry[1].extend(mqopt)
3597
3601
3598 dotable(commands.table)
3602 dotable(commands.table)
3599
3603
3600 for extname, extmodule in extensions.extensions():
3604 for extname, extmodule in extensions.extensions():
3601 if extmodule.__file__ != __file__:
3605 if extmodule.__file__ != __file__:
3602 dotable(getattr(extmodule, 'cmdtable', {}))
3606 dotable(getattr(extmodule, 'cmdtable', {}))
3603
3607
3604 colortable = {'qguard.negative': 'red',
3608 colortable = {'qguard.negative': 'red',
3605 'qguard.positive': 'yellow',
3609 'qguard.positive': 'yellow',
3606 'qguard.unguarded': 'green',
3610 'qguard.unguarded': 'green',
3607 'qseries.applied': 'blue bold underline',
3611 'qseries.applied': 'blue bold underline',
3608 'qseries.guarded': 'black bold',
3612 'qseries.guarded': 'black bold',
3609 'qseries.missing': 'red bold',
3613 'qseries.missing': 'red bold',
3610 'qseries.unapplied': 'black bold'}
3614 'qseries.unapplied': 'black bold'}
@@ -1,342 +1,359 b''
1 #require killdaemons
1 #require killdaemons
2
2
3 $ cat > writelines.py <<EOF
3 $ cat > writelines.py <<EOF
4 > import sys
4 > import sys
5 > path = sys.argv[1]
5 > path = sys.argv[1]
6 > args = sys.argv[2:]
6 > args = sys.argv[2:]
7 > assert (len(args) % 2) == 0
7 > assert (len(args) % 2) == 0
8 >
8 >
9 > f = file(path, 'wb')
9 > f = file(path, 'wb')
10 > for i in xrange(len(args)/2):
10 > for i in xrange(len(args)/2):
11 > count, s = args[2*i:2*i+2]
11 > count, s = args[2*i:2*i+2]
12 > count = int(count)
12 > count = int(count)
13 > s = s.decode('string_escape')
13 > s = s.decode('string_escape')
14 > f.write(s*count)
14 > f.write(s*count)
15 > f.close()
15 > f.close()
16 >
16 >
17 > EOF
17 > EOF
18 > cat <<EOF >> $HGRCPATH
18 > cat <<EOF >> $HGRCPATH
19 > [extensions]
19 > [extensions]
20 > mq =
20 > mq =
21 > [diff]
21 > [diff]
22 > git = 1
22 > git = 1
23 > EOF
23 > EOF
24 $ hg init repo
24 $ hg init repo
25 $ cd repo
25 $ cd repo
26
26
27 qimport without file or revision
27 qimport without file or revision
28
28
29 $ hg qimport
29 $ hg qimport
30 abort: no files or revisions specified
30 abort: no files or revisions specified
31 [255]
31 [255]
32
32
33 qimport non-existing-file
33 qimport non-existing-file
34
34
35 $ hg qimport non-existing-file
35 $ hg qimport non-existing-file
36 abort: unable to read file non-existing-file
36 abort: unable to read file non-existing-file
37 [255]
37 [255]
38
38
39 qimport null revision
39 qimport null revision
40
40
41 $ hg qimport -r null
41 $ hg qimport -r null
42 abort: revision -1 is not mutable
42 abort: revision -1 is not mutable
43 (see 'hg help phases' for details)
43 (see 'hg help phases' for details)
44 [255]
44 [255]
45 $ hg qseries
45 $ hg qseries
46
46
47 import email
47 import email
48
48
49 $ hg qimport --push -n email - <<EOF
49 $ hg qimport --push -n email - <<EOF
50 > From: Username in email <test@example.net>
50 > From: Username in email <test@example.net>
51 > Subject: [PATCH] Message in email
51 > Subject: [PATCH] Message in email
52 > Date: Fri, 02 Jan 1970 00:00:00 +0000
52 > Date: Fri, 02 Jan 1970 00:00:00 +0000
53 >
53 >
54 > Text before patch.
54 > Text before patch.
55 >
55 >
56 > # HG changeset patch
56 > # HG changeset patch
57 > # User Username in patch <test@example.net>
57 > # User Username in patch <test@example.net>
58 > # Date 0 0
58 > # Date 0 0
59 > # Node ID 1a706973a7d84cb549823634a821d9bdf21c6220
59 > # Node ID 1a706973a7d84cb549823634a821d9bdf21c6220
60 > # Parent 0000000000000000000000000000000000000000
60 > # Parent 0000000000000000000000000000000000000000
61 > First line of commit message.
61 > First line of commit message.
62 >
62 >
63 > More text in commit message.
63 > More text in commit message.
64 > --- confuse the diff detection
64 > --- confuse the diff detection
65 >
65 >
66 > diff --git a/x b/x
66 > diff --git a/x b/x
67 > new file mode 100644
67 > new file mode 100644
68 > --- /dev/null
68 > --- /dev/null
69 > +++ b/x
69 > +++ b/x
70 > @@ -0,0 +1,1 @@
70 > @@ -0,0 +1,1 @@
71 > +new file
71 > +new file
72 > Text after patch.
72 > Text after patch.
73 >
73 >
74 > EOF
74 > EOF
75 adding email to series file
75 adding email to series file
76 applying email
76 applying email
77 now at: email
77 now at: email
78
78
79 hg tip -v
79 hg tip -v
80
80
81 $ hg tip -v
81 $ hg tip -v
82 changeset: 0:1a706973a7d8
82 changeset: 0:1a706973a7d8
83 tag: email
83 tag: email
84 tag: qbase
84 tag: qbase
85 tag: qtip
85 tag: qtip
86 tag: tip
86 tag: tip
87 user: Username in patch <test@example.net>
87 user: Username in patch <test@example.net>
88 date: Thu Jan 01 00:00:00 1970 +0000
88 date: Thu Jan 01 00:00:00 1970 +0000
89 files: x
89 files: x
90 description:
90 description:
91 First line of commit message.
91 First line of commit message.
92
92
93 More text in commit message.
93 More text in commit message.
94
94
95
95
96 $ hg qpop
96 $ hg qpop
97 popping email
97 popping email
98 patch queue now empty
98 patch queue now empty
99 $ hg qdelete email
99 $ hg qdelete email
100
100
101 import URL
101 import URL
102
102
103 $ echo foo >> foo
103 $ echo foo >> foo
104 $ hg add foo
104 $ hg add foo
105 $ hg diff > url.diff
105 $ hg diff > url.diff
106 $ hg revert --no-backup foo
106 $ hg revert --no-backup foo
107 $ rm foo
107 $ rm foo
108
108
109 Under unix: file:///foobar/blah
109 Under unix: file:///foobar/blah
110 Under windows: file:///c:/foobar/blah
110 Under windows: file:///c:/foobar/blah
111
111
112 $ patchurl=`pwd | tr '\\\\' /`/url.diff
112 $ patchurl=`pwd | tr '\\\\' /`/url.diff
113 $ expr "$patchurl" : "\/" > /dev/null || patchurl="/$patchurl"
113 $ expr "$patchurl" : "\/" > /dev/null || patchurl="/$patchurl"
114 $ hg qimport file://"$patchurl"
114 $ hg qimport file://"$patchurl"
115 adding url.diff to series file
115 adding url.diff to series file
116 $ rm url.diff
116 $ rm url.diff
117 $ hg qun
117 $ hg qun
118 url.diff
118 url.diff
119
119
120 import patch that already exists
120 import patch that already exists
121
121
122 $ echo foo2 >> foo
122 $ echo foo2 >> foo
123 $ hg add foo
123 $ hg add foo
124 $ hg diff > ../url.diff
124 $ hg diff > ../url.diff
125 $ hg revert --no-backup foo
125 $ hg revert --no-backup foo
126 $ rm foo
126 $ rm foo
127 $ hg qimport ../url.diff
127 $ hg qimport ../url.diff
128 abort: patch "url.diff" already exists
128 abort: patch "url.diff" already exists
129 [255]
129 [255]
130 $ hg qpush
130 $ hg qpush
131 applying url.diff
131 applying url.diff
132 now at: url.diff
132 now at: url.diff
133 $ cat foo
133 $ cat foo
134 foo
134 foo
135 $ hg qpop
135 $ hg qpop
136 popping url.diff
136 popping url.diff
137 patch queue now empty
137 patch queue now empty
138
138
139 qimport -f
139 qimport -f
140
140
141 $ hg qimport -f ../url.diff
141 $ hg qimport -f ../url.diff
142 adding url.diff to series file
142 adding url.diff to series file
143 $ hg qpush
143 $ hg qpush
144 applying url.diff
144 applying url.diff
145 now at: url.diff
145 now at: url.diff
146 $ cat foo
146 $ cat foo
147 foo2
147 foo2
148 $ hg qpop
148 $ hg qpop
149 popping url.diff
149 popping url.diff
150 patch queue now empty
150 patch queue now empty
151
151
152 build diff with CRLF
152 build diff with CRLF
153
153
154 $ python ../writelines.py b 5 'a\n' 5 'a\r\n'
154 $ python ../writelines.py b 5 'a\n' 5 'a\r\n'
155 $ hg ci -Am addb
155 $ hg ci -Am addb
156 adding b
156 adding b
157 $ python ../writelines.py b 2 'a\n' 10 'b\n' 2 'a\r\n'
157 $ python ../writelines.py b 2 'a\n' 10 'b\n' 2 'a\r\n'
158 $ hg diff > b.diff
158 $ hg diff > b.diff
159 $ hg up -C
159 $ hg up -C
160 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
161
161
162 qimport CRLF diff
162 qimport CRLF diff
163
163
164 $ hg qimport b.diff
164 $ hg qimport b.diff
165 adding b.diff to series file
165 adding b.diff to series file
166 $ hg qpush
166 $ hg qpush
167 applying b.diff
167 applying b.diff
168 now at: b.diff
168 now at: b.diff
169
169
170 try to import --push
170 try to import --push
171
171
172 $ cat > appendfoo.diff <<EOF
172 $ cat > appendfoo.diff <<EOF
173 > append foo
173 > append foo
174 >
174 >
175 > diff -r 07f494440405 -r 261500830e46 baz
175 > diff -r 07f494440405 -r 261500830e46 baz
176 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
176 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
177 > +++ b/baz Thu Jan 01 00:00:00 1970 +0000
177 > +++ b/baz Thu Jan 01 00:00:00 1970 +0000
178 > @@ -0,0 +1,1 @@
178 > @@ -0,0 +1,1 @@
179 > +foo
179 > +foo
180 > EOF
180 > EOF
181
181
182 $ cat > appendbar.diff <<EOF
182 $ cat > appendbar.diff <<EOF
183 > append bar
183 > append bar
184 >
184 >
185 > diff -r 07f494440405 -r 261500830e46 baz
185 > diff -r 07f494440405 -r 261500830e46 baz
186 > --- a/baz Thu Jan 01 00:00:00 1970 +0000
186 > --- a/baz Thu Jan 01 00:00:00 1970 +0000
187 > +++ b/baz Thu Jan 01 00:00:00 1970 +0000
187 > +++ b/baz Thu Jan 01 00:00:00 1970 +0000
188 > @@ -1,1 +1,2 @@
188 > @@ -1,1 +1,2 @@
189 > foo
189 > foo
190 > +bar
190 > +bar
191 > EOF
191 > EOF
192
192
193 $ hg qimport --push appendfoo.diff appendbar.diff
193 $ hg qimport --push appendfoo.diff appendbar.diff
194 adding appendfoo.diff to series file
194 adding appendfoo.diff to series file
195 adding appendbar.diff to series file
195 adding appendbar.diff to series file
196 applying appendfoo.diff
196 applying appendfoo.diff
197 applying appendbar.diff
197 applying appendbar.diff
198 now at: appendbar.diff
198 now at: appendbar.diff
199 $ hg qfin -a
199 $ hg qfin -a
200 patch b.diff finalized without changeset message
200 patch b.diff finalized without changeset message
201 $ touch .hg/patches/append_foo
201 $ touch .hg/patches/append_foo
202 $ hg qimport -r 'p1(.)::'
202 $ hg qimport -r 'p1(.)::'
203 $ hg qapplied
203 $ hg qapplied
204 append_foo__1
204 append_foo__1
205 append_bar
205 append_bar
206 $ hg qfin -a
206 $ hg qfin -a
207 $ rm .hg/patches/append_foo
207 $ rm .hg/patches/append_foo
208 $ hg qimport -r 'p1(.)::' -P
208 $ hg qimport -r 'p1(.)::' -P
209 $ hg qpop -a
209 $ hg qpop -a
210 popping append_bar
210 popping append_bar
211 popping append_foo
211 popping append_foo
212 patch queue now empty
212 patch queue now empty
213 $ hg qdel append_foo
213 $ hg qdel append_foo
214 $ hg qdel -k append_bar
214 $ hg qdel -k append_bar
215
215
216 qimport -e
216 qimport -e
217
217
218 $ hg qimport -e append_bar
218 $ hg qimport -e append_bar
219 adding append_bar to series file
219 adding append_bar to series file
220 $ hg qdel -k append_bar
220 $ hg qdel -k append_bar
221
221
222 qimport -e --name newname oldexisitingpatch
222 qimport -e --name newname oldexisitingpatch
223
223
224 $ hg qimport -e --name this-name-is-better append_bar
224 $ hg qimport -e --name this-name-is-better append_bar
225 renaming append_bar to this-name-is-better
225 renaming append_bar to this-name-is-better
226 adding this-name-is-better to series file
226 adding this-name-is-better to series file
227 $ hg qser
227 $ hg qser
228 this-name-is-better
228 this-name-is-better
229 url.diff
229 url.diff
230
230
231 qimport -e --name without --force
231 qimport -e --name without --force
232
232
233 $ cp .hg/patches/this-name-is-better .hg/patches/3.diff
233 $ cp .hg/patches/this-name-is-better .hg/patches/3.diff
234 $ hg qimport -e --name this-name-is-better 3.diff
234 $ hg qimport -e --name this-name-is-better 3.diff
235 abort: patch "this-name-is-better" already exists
235 abort: patch "this-name-is-better" already exists
236 [255]
236 [255]
237 $ hg qser
237 $ hg qser
238 this-name-is-better
238 this-name-is-better
239 url.diff
239 url.diff
240
240
241 qimport -e --name with --force
241 qimport -e --name with --force
242
242
243 $ hg qimport --force -e --name this-name-is-better 3.diff
243 $ hg qimport --force -e --name this-name-is-better 3.diff
244 renaming 3.diff to this-name-is-better
244 renaming 3.diff to this-name-is-better
245 adding this-name-is-better to series file
245 adding this-name-is-better to series file
246 $ hg qser
246 $ hg qser
247 this-name-is-better
247 this-name-is-better
248 url.diff
248 url.diff
249
249
250 import patch of bad filename
251
252 $ touch '../ bad.diff'
253 $ hg qimport '../ bad.diff'
254 abort: patch name cannot begin or end with whitespace
255 [255]
256 $ touch '.hg/patches/ bad.diff'
257 $ hg qimport -e ' bad.diff'
258 abort: patch name cannot begin or end with whitespace
259 [255]
260
250 qimport with bad name, should abort before reading file
261 qimport with bad name, should abort before reading file
251
262
252 $ hg qimport non-existent-file --name .hg
263 $ hg qimport non-existent-file --name .hg
253 abort: patch name cannot begin with ".hg"
264 abort: patch name cannot begin with ".hg"
254 [255]
265 [255]
266 $ hg qimport non-existent-file --name ' foo'
267 abort: patch name cannot begin or end with whitespace
268 [255]
269 $ hg qimport non-existent-file --name 'foo '
270 abort: patch name cannot begin or end with whitespace
271 [255]
255
272
256 qimport http:// patch with leading slashes in url
273 qimport http:// patch with leading slashes in url
257
274
258 set up hgweb
275 set up hgweb
259
276
260 $ cd ..
277 $ cd ..
261 $ hg init served
278 $ hg init served
262 $ cd served
279 $ cd served
263 $ echo a > a
280 $ echo a > a
264 $ hg ci -Am patch
281 $ hg ci -Am patch
265 adding a
282 adding a
266 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
283 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
267 $ cat hg.pid >> $DAEMON_PIDS
284 $ cat hg.pid >> $DAEMON_PIDS
268
285
269 $ cd ../repo
286 $ cd ../repo
270 $ hg qimport http://localhost:$HGPORT/raw-rev/0///
287 $ hg qimport http://localhost:$HGPORT/raw-rev/0///
271 adding 0 to series file
288 adding 0 to series file
272
289
273 check qimport phase:
290 check qimport phase:
274
291
275 $ hg -q qpush
292 $ hg -q qpush
276 now at: 0
293 now at: 0
277 $ hg phase qparent
294 $ hg phase qparent
278 1: draft
295 1: draft
279 $ hg qimport -r qparent
296 $ hg qimport -r qparent
280 $ hg phase qbase
297 $ hg phase qbase
281 1: draft
298 1: draft
282 $ hg qfinish qbase
299 $ hg qfinish qbase
283 $ echo '[mq]' >> $HGRCPATH
300 $ echo '[mq]' >> $HGRCPATH
284 $ echo 'secret=true' >> $HGRCPATH
301 $ echo 'secret=true' >> $HGRCPATH
285 $ hg qimport -r qparent
302 $ hg qimport -r qparent
286 $ hg phase qbase
303 $ hg phase qbase
287 1: secret
304 1: secret
288
305
289 $ cd ..
306 $ cd ..
290
307
291 $ killdaemons.py
308 $ killdaemons.py
292
309
293 check patch name generation for non-alpha-numeric summary line
310 check patch name generation for non-alpha-numeric summary line
294
311
295 $ cd repo
312 $ cd repo
296
313
297 $ hg qpop -a -q
314 $ hg qpop -a -q
298 patch queue now empty
315 patch queue now empty
299 $ hg qseries -v
316 $ hg qseries -v
300 0 U imported_patch_b_diff
317 0 U imported_patch_b_diff
301 1 U 0
318 1 U 0
302 2 U this-name-is-better
319 2 U this-name-is-better
303 3 U url.diff
320 3 U url.diff
304
321
305 $ echo bb >> b
322 $ echo bb >> b
306 $ hg commit -m '==++--=='
323 $ hg commit -m '==++--=='
307
324
308 $ hg qimport -r tip
325 $ hg qimport -r tip
309 $ hg qseries -v
326 $ hg qseries -v
310 0 A 1.diff
327 0 A 1.diff
311 1 U imported_patch_b_diff
328 1 U imported_patch_b_diff
312 2 U 0
329 2 U 0
313 3 U this-name-is-better
330 3 U this-name-is-better
314 4 U url.diff
331 4 U url.diff
315
332
316 check reserved patch names
333 check reserved patch names
317
334
318 $ hg qpop -qa
335 $ hg qpop -qa
319 patch queue now empty
336 patch queue now empty
320 $ echo >> b
337 $ echo >> b
321 $ hg commit -m 'status'
338 $ hg commit -m 'status'
322 $ echo >> b
339 $ echo >> b
323 $ hg commit -m '.'
340 $ hg commit -m '.'
324 $ echo >> b
341 $ echo >> b
325 $ hg commit -m 'taken'
342 $ hg commit -m 'taken'
326 $ mkdir .hg/patches/taken
343 $ mkdir .hg/patches/taken
327 $ touch .hg/patches/taken__1
344 $ touch .hg/patches/taken__1
328 $ hg qimport -r -3::
345 $ hg qimport -r -3::
329 $ hg qap
346 $ hg qap
330 1.diff__1
347 1.diff__1
331 2.diff
348 2.diff
332 taken__2
349 taken__2
333
350
334 check very long patch name
351 check very long patch name
335
352
336 $ hg qpop -qa
353 $ hg qpop -qa
337 patch queue now empty
354 patch queue now empty
338 $ echo >> b
355 $ echo >> b
339 $ hg commit -m 'abcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
356 $ hg commit -m 'abcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
340 $ hg qimport -r .
357 $ hg qimport -r .
341 $ hg qap
358 $ hg qap
342 abcdefghi_pqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi_pqrstuvwxyzabcdefg
359 abcdefghi_pqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi_pqrstuvwxyzabcdefg
@@ -1,348 +1,321 b''
1
1
2 $ catpatch() {
2 $ catpatch() {
3 > cat $1 | sed -e "s/^\(# Parent \).*/\1/"
3 > cat $1 | sed -e "s/^\(# Parent \).*/\1/"
4 > }
4 > }
5 $ echo "[extensions]" >> $HGRCPATH
5 $ echo "[extensions]" >> $HGRCPATH
6 $ echo "mq=" >> $HGRCPATH
6 $ echo "mq=" >> $HGRCPATH
7 $ runtest() {
7 $ runtest() {
8 > hg init mq
8 > hg init mq
9 > cd mq
9 > cd mq
10 >
10 >
11 > echo a > a
11 > echo a > a
12 > hg ci -Ama
12 > hg ci -Ama
13 >
13 >
14 > echo '% qnew should refuse bad patch names'
14 > echo '% qnew should refuse bad patch names'
15 > hg qnew series
15 > hg qnew series
16 > hg qnew status
16 > hg qnew status
17 > hg qnew guards
17 > hg qnew guards
18 > hg qnew .
18 > hg qnew .
19 > hg qnew ..
19 > hg qnew ..
20 > hg qnew .hgignore
20 > hg qnew .hgignore
21 > hg qnew .mqfoo
21 > hg qnew .mqfoo
22 > hg qnew 'foo#bar'
22 > hg qnew 'foo#bar'
23 > hg qnew 'foo:bar'
23 > hg qnew 'foo:bar'
24 > hg qnew "`echo foo; echo bar`"
24 > hg qnew "`echo foo; echo bar`"
25 > hg qnew ' foo'
26 > hg qnew 'foo '
25 >
27 >
26 > hg qinit -c
28 > hg qinit -c
27 >
29 >
28 > echo '% qnew with name containing slash'
30 > echo '% qnew with name containing slash'
29 > hg qnew foo/
31 > hg qnew foo/
30 > hg qnew foo/bar.patch
32 > hg qnew foo/bar.patch
31 > hg qnew foo
33 > hg qnew foo
32 > hg qseries
34 > hg qseries
33 > hg qpop
35 > hg qpop
34 > hg qdelete foo/bar.patch
36 > hg qdelete foo/bar.patch
35 >
37 >
36 > echo '% qnew with uncommitted changes'
38 > echo '% qnew with uncommitted changes'
37 > echo a > somefile
39 > echo a > somefile
38 > hg add somefile
40 > hg add somefile
39 > hg qnew uncommitted.patch
41 > hg qnew uncommitted.patch
40 > hg st
42 > hg st
41 > hg qseries
43 > hg qseries
42 >
44 >
43 > echo '% qnew implies add'
45 > echo '% qnew implies add'
44 > hg -R .hg/patches st
46 > hg -R .hg/patches st
45 >
47 >
46 > echo '% qnew missing'
48 > echo '% qnew missing'
47 > hg qnew missing.patch missing
49 > hg qnew missing.patch missing
48 >
50 >
49 > echo '% qnew -m'
51 > echo '% qnew -m'
50 > hg qnew -m 'foo bar' mtest.patch
52 > hg qnew -m 'foo bar' mtest.patch
51 > catpatch .hg/patches/mtest.patch
53 > catpatch .hg/patches/mtest.patch
52 >
54 >
53 > echo '% qnew twice'
55 > echo '% qnew twice'
54 > hg qnew first.patch
56 > hg qnew first.patch
55 > hg qnew first.patch
57 > hg qnew first.patch
56 >
58 >
57 > touch ../first.patch
59 > touch ../first.patch
58 > hg qimport ../first.patch
60 > hg qimport ../first.patch
59 >
61 >
60 > echo '% qnew -f from a subdirectory'
62 > echo '% qnew -f from a subdirectory'
61 > hg qpop -a
63 > hg qpop -a
62 > mkdir d
64 > mkdir d
63 > cd d
65 > cd d
64 > echo b > b
66 > echo b > b
65 > hg ci -Am t
67 > hg ci -Am t
66 > echo b >> b
68 > echo b >> b
67 > hg st
69 > hg st
68 > hg qnew -g -f p
70 > hg qnew -g -f p
69 > catpatch ../.hg/patches/p
71 > catpatch ../.hg/patches/p
70 >
72 >
71 > echo '% qnew -u with no username configured'
73 > echo '% qnew -u with no username configured'
72 > HGUSER= hg qnew -u blue red
74 > HGUSER= hg qnew -u blue red
73 > catpatch ../.hg/patches/red
75 > catpatch ../.hg/patches/red
74 >
76 >
75 > echo '% qnew -e -u with no username configured'
77 > echo '% qnew -e -u with no username configured'
76 > HGUSER= hg qnew -e -u chartreuse fucsia
78 > HGUSER= hg qnew -e -u chartreuse fucsia
77 > catpatch ../.hg/patches/fucsia
79 > catpatch ../.hg/patches/fucsia
78 >
80 >
79 > echo '% fail when trying to import a merge'
81 > echo '% fail when trying to import a merge'
80 > hg init merge
82 > hg init merge
81 > cd merge
83 > cd merge
82 > touch a
84 > touch a
83 > hg ci -Am null
85 > hg ci -Am null
84 > echo a >> a
86 > echo a >> a
85 > hg ci -m a
87 > hg ci -m a
86 > hg up -r 0
88 > hg up -r 0
87 > echo b >> a
89 > echo b >> a
88 > hg ci -m b
90 > hg ci -m b
89 > hg merge -f 1
91 > hg merge -f 1
90 > hg resolve --mark a
92 > hg resolve --mark a
91 > hg qnew -f merge
93 > hg qnew -f merge
92 >
94 >
93 > cd ../../..
95 > cd ../../..
94 > rm -r mq
96 > rm -r mq
95 > }
97 > }
96
98
97 plain headers
99 plain headers
98
100
99 $ echo "[mq]" >> $HGRCPATH
101 $ echo "[mq]" >> $HGRCPATH
100 $ echo "plain=true" >> $HGRCPATH
102 $ echo "plain=true" >> $HGRCPATH
101 $ mkdir sandbox
103 $ mkdir sandbox
102 $ (cd sandbox ; runtest)
104 $ (cd sandbox ; runtest)
103 adding a
105 adding a
104 % qnew should refuse bad patch names
106 % qnew should refuse bad patch names
105 abort: "series" cannot be used as the name of a patch
107 abort: "series" cannot be used as the name of a patch
106 abort: "status" cannot be used as the name of a patch
108 abort: "status" cannot be used as the name of a patch
107 abort: "guards" cannot be used as the name of a patch
109 abort: "guards" cannot be used as the name of a patch
108 abort: "." cannot be used as the name of a patch
110 abort: "." cannot be used as the name of a patch
109 abort: ".." cannot be used as the name of a patch
111 abort: ".." cannot be used as the name of a patch
110 abort: patch name cannot begin with ".hg"
112 abort: patch name cannot begin with ".hg"
111 abort: patch name cannot begin with ".mq"
113 abort: patch name cannot begin with ".mq"
112 abort: '#' cannot be used in the name of a patch
114 abort: '#' cannot be used in the name of a patch
113 abort: ':' cannot be used in the name of a patch
115 abort: ':' cannot be used in the name of a patch
114 abort: '\n' cannot be used in the name of a patch
116 abort: '\n' cannot be used in the name of a patch
117 abort: patch name cannot begin or end with whitespace
118 abort: patch name cannot begin or end with whitespace
115 % qnew with name containing slash
119 % qnew with name containing slash
116 abort: path ends in directory separator: foo/ (glob)
120 abort: path ends in directory separator: foo/ (glob)
117 abort: "foo" already exists as a directory
121 abort: "foo" already exists as a directory
118 foo/bar.patch
122 foo/bar.patch
119 popping foo/bar.patch
123 popping foo/bar.patch
120 patch queue now empty
124 patch queue now empty
121 % qnew with uncommitted changes
125 % qnew with uncommitted changes
122 uncommitted.patch
126 uncommitted.patch
123 % qnew implies add
127 % qnew implies add
124 A .hgignore
128 A .hgignore
125 A series
129 A series
126 A uncommitted.patch
130 A uncommitted.patch
127 % qnew missing
131 % qnew missing
128 abort: missing: * (glob)
132 abort: missing: * (glob)
129 % qnew -m
133 % qnew -m
130 foo bar
134 foo bar
131
135
132 % qnew twice
136 % qnew twice
133 abort: patch "first.patch" already exists
137 abort: patch "first.patch" already exists
134 abort: patch "first.patch" already exists
138 abort: patch "first.patch" already exists
135 % qnew -f from a subdirectory
139 % qnew -f from a subdirectory
136 popping first.patch
140 popping first.patch
137 popping mtest.patch
141 popping mtest.patch
138 popping uncommitted.patch
142 popping uncommitted.patch
139 patch queue now empty
143 patch queue now empty
140 adding d/b
144 adding d/b
141 M d/b
145 M d/b
142 diff --git a/d/b b/d/b
146 diff --git a/d/b b/d/b
143 --- a/d/b
147 --- a/d/b
144 +++ b/d/b
148 +++ b/d/b
145 @@ -1,1 +1,2 @@
149 @@ -1,1 +1,2 @@
146 b
150 b
147 +b
151 +b
148 % qnew -u with no username configured
152 % qnew -u with no username configured
149 From: blue
153 From: blue
150
154
151 % qnew -e -u with no username configured
155 % qnew -e -u with no username configured
152 From: chartreuse
156 From: chartreuse
153
157
154 % fail when trying to import a merge
158 % fail when trying to import a merge
155 adding a
159 adding a
156 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 created new head
161 created new head
158 merging a
162 merging a
159 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
163 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
160 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
164 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
161 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
165 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
162 (no more unresolved files)
166 (no more unresolved files)
163 abort: cannot manage merge changesets
167 abort: cannot manage merge changesets
164 $ rm -r sandbox
168 $ rm -r sandbox
165
169
166 hg headers
170 hg headers
167
171
168 $ echo "plain=false" >> $HGRCPATH
172 $ echo "plain=false" >> $HGRCPATH
169 $ mkdir sandbox
173 $ mkdir sandbox
170 $ (cd sandbox ; runtest)
174 $ (cd sandbox ; runtest)
171 adding a
175 adding a
172 % qnew should refuse bad patch names
176 % qnew should refuse bad patch names
173 abort: "series" cannot be used as the name of a patch
177 abort: "series" cannot be used as the name of a patch
174 abort: "status" cannot be used as the name of a patch
178 abort: "status" cannot be used as the name of a patch
175 abort: "guards" cannot be used as the name of a patch
179 abort: "guards" cannot be used as the name of a patch
176 abort: "." cannot be used as the name of a patch
180 abort: "." cannot be used as the name of a patch
177 abort: ".." cannot be used as the name of a patch
181 abort: ".." cannot be used as the name of a patch
178 abort: patch name cannot begin with ".hg"
182 abort: patch name cannot begin with ".hg"
179 abort: patch name cannot begin with ".mq"
183 abort: patch name cannot begin with ".mq"
180 abort: '#' cannot be used in the name of a patch
184 abort: '#' cannot be used in the name of a patch
181 abort: ':' cannot be used in the name of a patch
185 abort: ':' cannot be used in the name of a patch
182 abort: '\n' cannot be used in the name of a patch
186 abort: '\n' cannot be used in the name of a patch
187 abort: patch name cannot begin or end with whitespace
188 abort: patch name cannot begin or end with whitespace
183 % qnew with name containing slash
189 % qnew with name containing slash
184 abort: path ends in directory separator: foo/ (glob)
190 abort: path ends in directory separator: foo/ (glob)
185 abort: "foo" already exists as a directory
191 abort: "foo" already exists as a directory
186 foo/bar.patch
192 foo/bar.patch
187 popping foo/bar.patch
193 popping foo/bar.patch
188 patch queue now empty
194 patch queue now empty
189 % qnew with uncommitted changes
195 % qnew with uncommitted changes
190 uncommitted.patch
196 uncommitted.patch
191 % qnew implies add
197 % qnew implies add
192 A .hgignore
198 A .hgignore
193 A series
199 A series
194 A uncommitted.patch
200 A uncommitted.patch
195 % qnew missing
201 % qnew missing
196 abort: missing: * (glob)
202 abort: missing: * (glob)
197 % qnew -m
203 % qnew -m
198 # HG changeset patch
204 # HG changeset patch
199 # Parent
205 # Parent
200 foo bar
206 foo bar
201
207
202 % qnew twice
208 % qnew twice
203 abort: patch "first.patch" already exists
209 abort: patch "first.patch" already exists
204 abort: patch "first.patch" already exists
210 abort: patch "first.patch" already exists
205 % qnew -f from a subdirectory
211 % qnew -f from a subdirectory
206 popping first.patch
212 popping first.patch
207 popping mtest.patch
213 popping mtest.patch
208 popping uncommitted.patch
214 popping uncommitted.patch
209 patch queue now empty
215 patch queue now empty
210 adding d/b
216 adding d/b
211 M d/b
217 M d/b
212 # HG changeset patch
218 # HG changeset patch
213 # Parent
219 # Parent
214
220
215 diff --git a/d/b b/d/b
221 diff --git a/d/b b/d/b
216 --- a/d/b
222 --- a/d/b
217 +++ b/d/b
223 +++ b/d/b
218 @@ -1,1 +1,2 @@
224 @@ -1,1 +1,2 @@
219 b
225 b
220 +b
226 +b
221 % qnew -u with no username configured
227 % qnew -u with no username configured
222 # HG changeset patch
228 # HG changeset patch
223 # User blue
229 # User blue
224 # Parent
230 # Parent
225
231
226 % qnew -e -u with no username configured
232 % qnew -e -u with no username configured
227 # HG changeset patch
233 # HG changeset patch
228 # User chartreuse
234 # User chartreuse
229 # Parent
235 # Parent
230
236
231 % fail when trying to import a merge
237 % fail when trying to import a merge
232 adding a
238 adding a
233 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 created new head
240 created new head
235 merging a
241 merging a
236 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
242 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
237 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
243 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
238 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
244 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
239 (no more unresolved files)
245 (no more unresolved files)
240 abort: cannot manage merge changesets
246 abort: cannot manage merge changesets
241 $ rm -r sandbox
247 $ rm -r sandbox
242
248
243 Test saving last-message.txt
249 Test saving last-message.txt
244
250
245 $ hg init repo
251 $ hg init repo
246 $ cd repo
252 $ cd repo
247
253
248 $ cat > $TESTTMP/commitfailure.py <<EOF
254 $ cat > $TESTTMP/commitfailure.py <<EOF
249 > from mercurial import error
255 > from mercurial import error
250 > def reposetup(ui, repo):
256 > def reposetup(ui, repo):
251 > class commitfailure(repo.__class__):
257 > class commitfailure(repo.__class__):
252 > def commit(self, *args, **kwargs):
258 > def commit(self, *args, **kwargs):
253 > raise error.Abort('emulating unexpected abort')
259 > raise error.Abort('emulating unexpected abort')
254 > repo.__class__ = commitfailure
260 > repo.__class__ = commitfailure
255 > EOF
261 > EOF
256 $ cat >> .hg/hgrc <<EOF
262 $ cat >> .hg/hgrc <<EOF
257 > [extensions]
263 > [extensions]
258 > # this failure occurs before editor invocation
264 > # this failure occurs before editor invocation
259 > commitfailure = $TESTTMP/commitfailure.py
265 > commitfailure = $TESTTMP/commitfailure.py
260 > EOF
266 > EOF
261
267
262 $ cat > $TESTTMP/editor.sh << EOF
268 $ cat > $TESTTMP/editor.sh << EOF
263 > echo "==== before editing"
269 > echo "==== before editing"
264 > cat \$1
270 > cat \$1
265 > echo "===="
271 > echo "===="
266 > echo "test saving last-message.txt" >> \$1
272 > echo "test saving last-message.txt" >> \$1
267 > EOF
273 > EOF
268
274
269 (test that editor is not invoked before transaction starting)
275 (test that editor is not invoked before transaction starting)
270
276
271 $ rm -f .hg/last-message.txt
277 $ rm -f .hg/last-message.txt
272 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qnew -e patch
278 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qnew -e patch
273 abort: emulating unexpected abort
279 abort: emulating unexpected abort
274 [255]
280 [255]
275 $ test -f .hg/last-message.txt
281 $ test -f .hg/last-message.txt
276 [1]
282 [1]
277
283
278 (test that editor is invoked and commit message is saved into
284 (test that editor is invoked and commit message is saved into
279 "last-message.txt")
285 "last-message.txt")
280
286
281 $ cat >> .hg/hgrc <<EOF
287 $ cat >> .hg/hgrc <<EOF
282 > [extensions]
288 > [extensions]
283 > commitfailure = !
289 > commitfailure = !
284 > [hooks]
290 > [hooks]
285 > # this failure occurs after editor invocation
291 > # this failure occurs after editor invocation
286 > pretxncommit.unexpectedabort = false
292 > pretxncommit.unexpectedabort = false
287 > EOF
293 > EOF
288
294
289 $ rm -f .hg/last-message.txt
295 $ rm -f .hg/last-message.txt
290 $ hg status
296 $ hg status
291 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qnew -e patch
297 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qnew -e patch
292 ==== before editing
298 ==== before editing
293
299
294
300
295 HG: Enter commit message. Lines beginning with 'HG:' are removed.
301 HG: Enter commit message. Lines beginning with 'HG:' are removed.
296 HG: Leave message empty to use default message.
302 HG: Leave message empty to use default message.
297 HG: --
303 HG: --
298 HG: user: test
304 HG: user: test
299 HG: branch 'default'
305 HG: branch 'default'
300 HG: no files changed
306 HG: no files changed
301 ====
307 ====
302 note: commit message saved in .hg/last-message.txt
308 note: commit message saved in .hg/last-message.txt
303 transaction abort!
309 transaction abort!
304 rollback completed
310 rollback completed
305 abort: pretxncommit.unexpectedabort hook exited with status 1
311 abort: pretxncommit.unexpectedabort hook exited with status 1
306 [255]
312 [255]
307 $ cat .hg/last-message.txt
313 $ cat .hg/last-message.txt
308
314
309
315
310 test saving last-message.txt
316 test saving last-message.txt
311
317
312 $ cat >> .hg/hgrc <<EOF
318 $ cat >> .hg/hgrc <<EOF
313 > [hooks]
319 > [hooks]
314 > pretxncommit.unexpectedabort =
320 > pretxncommit.unexpectedabort =
315 > EOF
321 > EOF
316
317 #if unix-permissions
318
319 Test handling default message with the patch filename with tail whitespaces
320
321 $ cat > $TESTTMP/editor.sh << EOF
322 > echo "==== before editing"
323 > cat \$1
324 > echo "===="
325 > echo "[mq]: patch " > \$1
326 > EOF
327
328 $ rm -f .hg/last-message.txt
329 $ hg status
330 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qnew -e "patch "
331 ==== before editing
332
333
334 HG: Enter commit message. Lines beginning with 'HG:' are removed.
335 HG: Leave message empty to use default message.
336 HG: --
337 HG: user: test
338 HG: branch 'default'
339 HG: no files changed
340 ====
341 $ cat ".hg/patches/patch "
342 # HG changeset patch
343 # Parent 0000000000000000000000000000000000000000
344
345
346 $ cd ..
347
348 #endif
@@ -1,416 +1,422 b''
1 Create configuration
1 Create configuration
2
2
3 $ echo "[ui]" >> $HGRCPATH
3 $ echo "[ui]" >> $HGRCPATH
4 $ echo "interactive=true" >> $HGRCPATH
4 $ echo "interactive=true" >> $HGRCPATH
5
5
6 help record (no record)
6 help record (no record)
7
7
8 $ hg help record
8 $ hg help record
9 record extension - commands to interactively select changes for
9 record extension - commands to interactively select changes for
10 commit/qrefresh (DEPRECATED)
10 commit/qrefresh (DEPRECATED)
11
11
12 The feature provided by this extension has been moved into core Mercurial as
12 The feature provided by this extension has been moved into core Mercurial as
13 'hg commit --interactive'.
13 'hg commit --interactive'.
14
14
15 (use 'hg help extensions' for information on enabling extensions)
15 (use 'hg help extensions' for information on enabling extensions)
16
16
17 help qrecord (no record)
17 help qrecord (no record)
18
18
19 $ hg help qrecord
19 $ hg help qrecord
20 'qrecord' is provided by the following extension:
20 'qrecord' is provided by the following extension:
21
21
22 record commands to interactively select changes for commit/qrefresh
22 record commands to interactively select changes for commit/qrefresh
23 (DEPRECATED)
23 (DEPRECATED)
24
24
25 (use 'hg help extensions' for information on enabling extensions)
25 (use 'hg help extensions' for information on enabling extensions)
26
26
27 $ echo "[extensions]" >> $HGRCPATH
27 $ echo "[extensions]" >> $HGRCPATH
28 $ echo "record=" >> $HGRCPATH
28 $ echo "record=" >> $HGRCPATH
29
29
30 help record (record)
30 help record (record)
31
31
32 $ hg help record
32 $ hg help record
33 hg record [OPTION]... [FILE]...
33 hg record [OPTION]... [FILE]...
34
34
35 interactively select changes to commit
35 interactively select changes to commit
36
36
37 If a list of files is omitted, all changes reported by 'hg status' will be
37 If a list of files is omitted, all changes reported by 'hg status' will be
38 candidates for recording.
38 candidates for recording.
39
39
40 See 'hg help dates' for a list of formats valid for -d/--date.
40 See 'hg help dates' for a list of formats valid for -d/--date.
41
41
42 If using the text interface (see 'hg help config'), you will be prompted
42 If using the text interface (see 'hg help config'), you will be prompted
43 for whether to record changes to each modified file, and for files with
43 for whether to record changes to each modified file, and for files with
44 multiple changes, for each change to use. For each query, the following
44 multiple changes, for each change to use. For each query, the following
45 responses are possible:
45 responses are possible:
46
46
47 y - record this change
47 y - record this change
48 n - skip this change
48 n - skip this change
49 e - edit this change manually
49 e - edit this change manually
50
50
51 s - skip remaining changes to this file
51 s - skip remaining changes to this file
52 f - record remaining changes to this file
52 f - record remaining changes to this file
53
53
54 d - done, skip remaining changes and files
54 d - done, skip remaining changes and files
55 a - record all changes to all remaining files
55 a - record all changes to all remaining files
56 q - quit, recording no changes
56 q - quit, recording no changes
57
57
58 ? - display help
58 ? - display help
59
59
60 This command is not available when committing a merge.
60 This command is not available when committing a merge.
61
61
62 (use 'hg help -e record' to show help for the record extension)
62 (use 'hg help -e record' to show help for the record extension)
63
63
64 options ([+] can be repeated):
64 options ([+] can be repeated):
65
65
66 -A --addremove mark new/missing files as added/removed before
66 -A --addremove mark new/missing files as added/removed before
67 committing
67 committing
68 --close-branch mark a branch head as closed
68 --close-branch mark a branch head as closed
69 --amend amend the parent of the working directory
69 --amend amend the parent of the working directory
70 -s --secret use the secret phase for committing
70 -s --secret use the secret phase for committing
71 -e --edit invoke editor on commit messages
71 -e --edit invoke editor on commit messages
72 -I --include PATTERN [+] include names matching the given patterns
72 -I --include PATTERN [+] include names matching the given patterns
73 -X --exclude PATTERN [+] exclude names matching the given patterns
73 -X --exclude PATTERN [+] exclude names matching the given patterns
74 -m --message TEXT use text as commit message
74 -m --message TEXT use text as commit message
75 -l --logfile FILE read commit message from file
75 -l --logfile FILE read commit message from file
76 -d --date DATE record the specified date as commit date
76 -d --date DATE record the specified date as commit date
77 -u --user USER record the specified user as committer
77 -u --user USER record the specified user as committer
78 -S --subrepos recurse into subrepositories
78 -S --subrepos recurse into subrepositories
79 -w --ignore-all-space ignore white space when comparing lines
79 -w --ignore-all-space ignore white space when comparing lines
80 -b --ignore-space-change ignore changes in the amount of white space
80 -b --ignore-space-change ignore changes in the amount of white space
81 -B --ignore-blank-lines ignore changes whose lines are all blank
81 -B --ignore-blank-lines ignore changes whose lines are all blank
82
82
83 (some details hidden, use --verbose to show complete help)
83 (some details hidden, use --verbose to show complete help)
84
84
85 help (no mq, so no qrecord)
85 help (no mq, so no qrecord)
86
86
87 $ hg help qrecord
87 $ hg help qrecord
88 hg qrecord [OPTION]... PATCH [FILE]...
88 hg qrecord [OPTION]... PATCH [FILE]...
89
89
90 interactively record a new patch
90 interactively record a new patch
91
91
92 See 'hg help qnew' & 'hg help record' for more information and usage.
92 See 'hg help qnew' & 'hg help record' for more information and usage.
93
93
94 (some details hidden, use --verbose to show complete help)
94 (some details hidden, use --verbose to show complete help)
95
95
96 $ hg init a
96 $ hg init a
97
97
98 qrecord (mq not present)
98 qrecord (mq not present)
99
99
100 $ hg -R a qrecord
100 $ hg -R a qrecord
101 hg qrecord: invalid arguments
101 hg qrecord: invalid arguments
102 hg qrecord [OPTION]... PATCH [FILE]...
102 hg qrecord [OPTION]... PATCH [FILE]...
103
103
104 interactively record a new patch
104 interactively record a new patch
105
105
106 (use 'hg qrecord -h' to show more help)
106 (use 'hg qrecord -h' to show more help)
107 [255]
107 [255]
108
108
109 qrecord patch (mq not present)
109 qrecord patch (mq not present)
110
110
111 $ hg -R a qrecord patch
111 $ hg -R a qrecord patch
112 abort: 'mq' extension not loaded
112 abort: 'mq' extension not loaded
113 [255]
113 [255]
114
114
115 help (bad mq)
115 help (bad mq)
116
116
117 $ echo "mq=nonexistent" >> $HGRCPATH
117 $ echo "mq=nonexistent" >> $HGRCPATH
118 $ hg help qrecord
118 $ hg help qrecord
119 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
119 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
120 hg qrecord [OPTION]... PATCH [FILE]...
120 hg qrecord [OPTION]... PATCH [FILE]...
121
121
122 interactively record a new patch
122 interactively record a new patch
123
123
124 See 'hg help qnew' & 'hg help record' for more information and usage.
124 See 'hg help qnew' & 'hg help record' for more information and usage.
125
125
126 (some details hidden, use --verbose to show complete help)
126 (some details hidden, use --verbose to show complete help)
127
127
128 help (mq present)
128 help (mq present)
129
129
130 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
130 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
131 $ mv hgrc.tmp $HGRCPATH
131 $ mv hgrc.tmp $HGRCPATH
132
132
133 $ hg help qrecord
133 $ hg help qrecord
134 hg qrecord [OPTION]... PATCH [FILE]...
134 hg qrecord [OPTION]... PATCH [FILE]...
135
135
136 interactively record a new patch
136 interactively record a new patch
137
137
138 See 'hg help qnew' & 'hg help record' for more information and usage.
138 See 'hg help qnew' & 'hg help record' for more information and usage.
139
139
140 options ([+] can be repeated):
140 options ([+] can be repeated):
141
141
142 -e --edit invoke editor on commit messages
142 -e --edit invoke editor on commit messages
143 -g --git use git extended diff format
143 -g --git use git extended diff format
144 -U --currentuser add "From: <current user>" to patch
144 -U --currentuser add "From: <current user>" to patch
145 -u --user USER add "From: <USER>" to patch
145 -u --user USER add "From: <USER>" to patch
146 -D --currentdate add "Date: <current date>" to patch
146 -D --currentdate add "Date: <current date>" to patch
147 -d --date DATE add "Date: <DATE>" to patch
147 -d --date DATE add "Date: <DATE>" to patch
148 -I --include PATTERN [+] include names matching the given patterns
148 -I --include PATTERN [+] include names matching the given patterns
149 -X --exclude PATTERN [+] exclude names matching the given patterns
149 -X --exclude PATTERN [+] exclude names matching the given patterns
150 -m --message TEXT use text as commit message
150 -m --message TEXT use text as commit message
151 -l --logfile FILE read commit message from file
151 -l --logfile FILE read commit message from file
152 -w --ignore-all-space ignore white space when comparing lines
152 -w --ignore-all-space ignore white space when comparing lines
153 -b --ignore-space-change ignore changes in the amount of white space
153 -b --ignore-space-change ignore changes in the amount of white space
154 -B --ignore-blank-lines ignore changes whose lines are all blank
154 -B --ignore-blank-lines ignore changes whose lines are all blank
155 --mq operate on patch repository
155 --mq operate on patch repository
156
156
157 (some details hidden, use --verbose to show complete help)
157 (some details hidden, use --verbose to show complete help)
158
158
159 $ cd a
159 $ cd a
160
160
161 Base commit
161 Base commit
162
162
163 $ cat > 1.txt <<EOF
163 $ cat > 1.txt <<EOF
164 > 1
164 > 1
165 > 2
165 > 2
166 > 3
166 > 3
167 > 4
167 > 4
168 > 5
168 > 5
169 > EOF
169 > EOF
170 $ cat > 2.txt <<EOF
170 $ cat > 2.txt <<EOF
171 > a
171 > a
172 > b
172 > b
173 > c
173 > c
174 > d
174 > d
175 > e
175 > e
176 > f
176 > f
177 > EOF
177 > EOF
178
178
179 $ mkdir dir
179 $ mkdir dir
180 $ cat > dir/a.txt <<EOF
180 $ cat > dir/a.txt <<EOF
181 > hello world
181 > hello world
182 >
182 >
183 > someone
183 > someone
184 > up
184 > up
185 > there
185 > there
186 > loves
186 > loves
187 > me
187 > me
188 > EOF
188 > EOF
189
189
190 $ hg add 1.txt 2.txt dir/a.txt
190 $ hg add 1.txt 2.txt dir/a.txt
191 $ hg commit -m 'initial checkin'
191 $ hg commit -m 'initial checkin'
192
192
193 Changing files
193 Changing files
194
194
195 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
195 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
196 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
196 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
197 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
197 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
198
198
199 $ mv -f 1.txt.new 1.txt
199 $ mv -f 1.txt.new 1.txt
200 $ mv -f 2.txt.new 2.txt
200 $ mv -f 2.txt.new 2.txt
201 $ mv -f dir/a.txt.new dir/a.txt
201 $ mv -f dir/a.txt.new dir/a.txt
202
202
203 Whole diff
203 Whole diff
204
204
205 $ hg diff --nodates
205 $ hg diff --nodates
206 diff -r 1057167b20ef 1.txt
206 diff -r 1057167b20ef 1.txt
207 --- a/1.txt
207 --- a/1.txt
208 +++ b/1.txt
208 +++ b/1.txt
209 @@ -1,5 +1,5 @@
209 @@ -1,5 +1,5 @@
210 1
210 1
211 -2
211 -2
212 +2 2
212 +2 2
213 3
213 3
214 -4
214 -4
215 +4 4
215 +4 4
216 5
216 5
217 diff -r 1057167b20ef 2.txt
217 diff -r 1057167b20ef 2.txt
218 --- a/2.txt
218 --- a/2.txt
219 +++ b/2.txt
219 +++ b/2.txt
220 @@ -1,5 +1,5 @@
220 @@ -1,5 +1,5 @@
221 a
221 a
222 -b
222 -b
223 +b b
223 +b b
224 c
224 c
225 d
225 d
226 e
226 e
227 diff -r 1057167b20ef dir/a.txt
227 diff -r 1057167b20ef dir/a.txt
228 --- a/dir/a.txt
228 --- a/dir/a.txt
229 +++ b/dir/a.txt
229 +++ b/dir/a.txt
230 @@ -1,4 +1,4 @@
230 @@ -1,4 +1,4 @@
231 -hello world
231 -hello world
232 +hello world!
232 +hello world!
233
233
234 someone
234 someone
235 up
235 up
236
236
237 qrecord with bad patch name, should abort before prompting
237 qrecord with bad patch name, should abort before prompting
238
238
239 $ hg qrecord .hg
239 $ hg qrecord .hg
240 abort: patch name cannot begin with ".hg"
240 abort: patch name cannot begin with ".hg"
241 [255]
241 [255]
242 $ hg qrecord ' foo'
243 abort: patch name cannot begin or end with whitespace
244 [255]
245 $ hg qrecord 'foo '
246 abort: patch name cannot begin or end with whitespace
247 [255]
242
248
243 qrecord a.patch
249 qrecord a.patch
244
250
245 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
251 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
246 > y
252 > y
247 > y
253 > y
248 > n
254 > n
249 > y
255 > y
250 > y
256 > y
251 > n
257 > n
252 > EOF
258 > EOF
253 diff --git a/1.txt b/1.txt
259 diff --git a/1.txt b/1.txt
254 2 hunks, 2 lines changed
260 2 hunks, 2 lines changed
255 examine changes to '1.txt'? [Ynesfdaq?] y
261 examine changes to '1.txt'? [Ynesfdaq?] y
256
262
257 @@ -1,3 +1,3 @@
263 @@ -1,3 +1,3 @@
258 1
264 1
259 -2
265 -2
260 +2 2
266 +2 2
261 3
267 3
262 record change 1/4 to '1.txt'? [Ynesfdaq?] y
268 record change 1/4 to '1.txt'? [Ynesfdaq?] y
263
269
264 @@ -3,3 +3,3 @@
270 @@ -3,3 +3,3 @@
265 3
271 3
266 -4
272 -4
267 +4 4
273 +4 4
268 5
274 5
269 record change 2/4 to '1.txt'? [Ynesfdaq?] n
275 record change 2/4 to '1.txt'? [Ynesfdaq?] n
270
276
271 diff --git a/2.txt b/2.txt
277 diff --git a/2.txt b/2.txt
272 1 hunks, 1 lines changed
278 1 hunks, 1 lines changed
273 examine changes to '2.txt'? [Ynesfdaq?] y
279 examine changes to '2.txt'? [Ynesfdaq?] y
274
280
275 @@ -1,5 +1,5 @@
281 @@ -1,5 +1,5 @@
276 a
282 a
277 -b
283 -b
278 +b b
284 +b b
279 c
285 c
280 d
286 d
281 e
287 e
282 record change 3/4 to '2.txt'? [Ynesfdaq?] y
288 record change 3/4 to '2.txt'? [Ynesfdaq?] y
283
289
284 diff --git a/dir/a.txt b/dir/a.txt
290 diff --git a/dir/a.txt b/dir/a.txt
285 1 hunks, 1 lines changed
291 1 hunks, 1 lines changed
286 examine changes to 'dir/a.txt'? [Ynesfdaq?] n
292 examine changes to 'dir/a.txt'? [Ynesfdaq?] n
287
293
288
294
289 After qrecord a.patch 'tip'"
295 After qrecord a.patch 'tip'"
290
296
291 $ hg tip -p
297 $ hg tip -p
292 changeset: 1:5d1ca63427ee
298 changeset: 1:5d1ca63427ee
293 tag: a.patch
299 tag: a.patch
294 tag: qbase
300 tag: qbase
295 tag: qtip
301 tag: qtip
296 tag: tip
302 tag: tip
297 user: test
303 user: test
298 date: Thu Jan 01 00:00:00 1970 +0000
304 date: Thu Jan 01 00:00:00 1970 +0000
299 summary: aaa
305 summary: aaa
300
306
301 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
307 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
302 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
308 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
303 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
309 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
304 @@ -1,5 +1,5 @@
310 @@ -1,5 +1,5 @@
305 1
311 1
306 -2
312 -2
307 +2 2
313 +2 2
308 3
314 3
309 4
315 4
310 5
316 5
311 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
317 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
312 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
318 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
313 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
319 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
314 @@ -1,5 +1,5 @@
320 @@ -1,5 +1,5 @@
315 a
321 a
316 -b
322 -b
317 +b b
323 +b b
318 c
324 c
319 d
325 d
320 e
326 e
321
327
322
328
323 After qrecord a.patch 'diff'"
329 After qrecord a.patch 'diff'"
324
330
325 $ hg diff --nodates
331 $ hg diff --nodates
326 diff -r 5d1ca63427ee 1.txt
332 diff -r 5d1ca63427ee 1.txt
327 --- a/1.txt
333 --- a/1.txt
328 +++ b/1.txt
334 +++ b/1.txt
329 @@ -1,5 +1,5 @@
335 @@ -1,5 +1,5 @@
330 1
336 1
331 2 2
337 2 2
332 3
338 3
333 -4
339 -4
334 +4 4
340 +4 4
335 5
341 5
336 diff -r 5d1ca63427ee dir/a.txt
342 diff -r 5d1ca63427ee dir/a.txt
337 --- a/dir/a.txt
343 --- a/dir/a.txt
338 +++ b/dir/a.txt
344 +++ b/dir/a.txt
339 @@ -1,4 +1,4 @@
345 @@ -1,4 +1,4 @@
340 -hello world
346 -hello world
341 +hello world!
347 +hello world!
342
348
343 someone
349 someone
344 up
350 up
345
351
346 qrecord b.patch
352 qrecord b.patch
347
353
348 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
354 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
349 > y
355 > y
350 > y
356 > y
351 > y
357 > y
352 > y
358 > y
353 > EOF
359 > EOF
354 diff --git a/1.txt b/1.txt
360 diff --git a/1.txt b/1.txt
355 1 hunks, 1 lines changed
361 1 hunks, 1 lines changed
356 examine changes to '1.txt'? [Ynesfdaq?] y
362 examine changes to '1.txt'? [Ynesfdaq?] y
357
363
358 @@ -1,5 +1,5 @@
364 @@ -1,5 +1,5 @@
359 1
365 1
360 2 2
366 2 2
361 3
367 3
362 -4
368 -4
363 +4 4
369 +4 4
364 5
370 5
365 record change 1/2 to '1.txt'? [Ynesfdaq?] y
371 record change 1/2 to '1.txt'? [Ynesfdaq?] y
366
372
367 diff --git a/dir/a.txt b/dir/a.txt
373 diff --git a/dir/a.txt b/dir/a.txt
368 1 hunks, 1 lines changed
374 1 hunks, 1 lines changed
369 examine changes to 'dir/a.txt'? [Ynesfdaq?] y
375 examine changes to 'dir/a.txt'? [Ynesfdaq?] y
370
376
371 @@ -1,4 +1,4 @@
377 @@ -1,4 +1,4 @@
372 -hello world
378 -hello world
373 +hello world!
379 +hello world!
374
380
375 someone
381 someone
376 up
382 up
377 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?] y
383 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?] y
378
384
379
385
380 After qrecord b.patch 'tip'
386 After qrecord b.patch 'tip'
381
387
382 $ hg tip -p
388 $ hg tip -p
383 changeset: 2:b056198bf878
389 changeset: 2:b056198bf878
384 tag: b.patch
390 tag: b.patch
385 tag: qtip
391 tag: qtip
386 tag: tip
392 tag: tip
387 user: test
393 user: test
388 date: Thu Jan 01 00:00:00 1970 +0000
394 date: Thu Jan 01 00:00:00 1970 +0000
389 summary: bbb
395 summary: bbb
390
396
391 diff -r 5d1ca63427ee -r b056198bf878 1.txt
397 diff -r 5d1ca63427ee -r b056198bf878 1.txt
392 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
398 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
393 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
399 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
394 @@ -1,5 +1,5 @@
400 @@ -1,5 +1,5 @@
395 1
401 1
396 2 2
402 2 2
397 3
403 3
398 -4
404 -4
399 +4 4
405 +4 4
400 5
406 5
401 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
407 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
402 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
408 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
403 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
409 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
404 @@ -1,4 +1,4 @@
410 @@ -1,4 +1,4 @@
405 -hello world
411 -hello world
406 +hello world!
412 +hello world!
407
413
408 someone
414 someone
409 up
415 up
410
416
411
417
412 After qrecord b.patch 'diff'
418 After qrecord b.patch 'diff'
413
419
414 $ hg diff --nodates
420 $ hg diff --nodates
415
421
416 $ cd ..
422 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now