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