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