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