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