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