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