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