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