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