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