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