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