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