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