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