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