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