##// END OF EJS Templates
mq: fix typo, thanks pmezard
Dirkjan Ochtman -
r10370:c050b7e4 default
parent child Browse files
Show More
@@ -1,2766 +1,2766
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:
787 if m or a or r or d:
788 if not force:
788 if not force:
789 if refresh:
789 if refresh:
790 raise util.Abort(_("local changes found, refresh first"))
790 raise util.Abort(_("local changes found, refresh first"))
791 else:
791 else:
792 raise util.Abort(_("local changes found"))
792 raise util.Abort(_("local changes found"))
793 return m, a, r, d
793 return m, a, r, d
794
794
795 _reserved = ('series', 'status', 'guards')
795 _reserved = ('series', 'status', 'guards')
796 def check_reserved_name(self, name):
796 def check_reserved_name(self, name):
797 if (name in self._reserved or name.startswith('.hg')
797 if (name in self._reserved or name.startswith('.hg')
798 or name.startswith('.mq')):
798 or name.startswith('.mq')):
799 raise util.Abort(_('"%s" cannot be used as the name of a patch')
799 raise util.Abort(_('"%s" cannot be used as the name of a patch')
800 % name)
800 % name)
801
801
802 def new(self, repo, patchfn, *pats, **opts):
802 def new(self, repo, patchfn, *pats, **opts):
803 """options:
803 """options:
804 msg: a string or a no-argument function returning a string
804 msg: a string or a no-argument function returning a string
805 """
805 """
806 msg = opts.get('msg')
806 msg = opts.get('msg')
807 force = opts.get('force')
807 force = opts.get('force')
808 user = opts.get('user')
808 user = opts.get('user')
809 date = opts.get('date')
809 date = opts.get('date')
810 if date:
810 if date:
811 date = util.parsedate(date)
811 date = util.parsedate(date)
812 diffopts = self.diffopts({'git': opts.get('git')})
812 diffopts = self.diffopts({'git': opts.get('git')})
813 self.check_reserved_name(patchfn)
813 self.check_reserved_name(patchfn)
814 if os.path.exists(self.join(patchfn)):
814 if os.path.exists(self.join(patchfn)):
815 raise util.Abort(_('patch "%s" already exists') % patchfn)
815 raise util.Abort(_('patch "%s" already exists') % patchfn)
816 if opts.get('include') or opts.get('exclude') or pats:
816 if opts.get('include') or opts.get('exclude') or pats:
817 match = cmdutil.match(repo, pats, opts)
817 match = cmdutil.match(repo, pats, opts)
818 # detect missing files in pats
818 # detect missing files in pats
819 def badfn(f, msg):
819 def badfn(f, msg):
820 raise util.Abort('%s: %s' % (f, msg))
820 raise util.Abort('%s: %s' % (f, msg))
821 match.bad = badfn
821 match.bad = badfn
822 m, a, r, d = repo.status(match=match)[:4]
822 m, a, r, d = repo.status(match=match)[:4]
823 else:
823 else:
824 m, a, r, d = self.check_localchanges(repo, force)
824 m, a, r, d = self.check_localchanges(repo, force)
825 match = cmdutil.matchfiles(repo, m + a + r)
825 match = cmdutil.matchfiles(repo, m + a + r)
826 if force:
826 if force:
827 p = repo[None].parents()
827 p = repo[None].parents()
828 if len(p) > 1:
828 if len(p) > 1:
829 raise util.Abort(_('cannot manage merge changesets'))
829 raise util.Abort(_('cannot manage merge changesets'))
830 commitfiles = m + a + r
830 commitfiles = m + a + r
831 self.check_toppatch(repo)
831 self.check_toppatch(repo)
832 insert = self.full_series_end()
832 insert = self.full_series_end()
833 wlock = repo.wlock()
833 wlock = repo.wlock()
834 try:
834 try:
835 # if patch file write fails, abort early
835 # if patch file write fails, abort early
836 p = self.opener(patchfn, "w")
836 p = self.opener(patchfn, "w")
837 try:
837 try:
838 if date:
838 if date:
839 p.write("# HG changeset patch\n")
839 p.write("# HG changeset patch\n")
840 if user:
840 if user:
841 p.write("# User " + user + "\n")
841 p.write("# User " + user + "\n")
842 p.write("# Date %d %d\n\n" % date)
842 p.write("# Date %d %d\n\n" % date)
843 elif user:
843 elif user:
844 p.write("From: " + user + "\n\n")
844 p.write("From: " + user + "\n\n")
845
845
846 if hasattr(msg, '__call__'):
846 if hasattr(msg, '__call__'):
847 msg = msg()
847 msg = msg()
848 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
848 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
849 n = repo.commit(commitmsg, user, date, match=match, force=True)
849 n = repo.commit(commitmsg, user, date, match=match, force=True)
850 if n is None:
850 if n is None:
851 raise util.Abort(_("repo commit failed"))
851 raise util.Abort(_("repo commit failed"))
852 try:
852 try:
853 self.full_series[insert:insert] = [patchfn]
853 self.full_series[insert:insert] = [patchfn]
854 self.applied.append(statusentry(hex(n), patchfn))
854 self.applied.append(statusentry(hex(n), patchfn))
855 self.parse_series()
855 self.parse_series()
856 self.series_dirty = 1
856 self.series_dirty = 1
857 self.applied_dirty = 1
857 self.applied_dirty = 1
858 if msg:
858 if msg:
859 msg = msg + "\n\n"
859 msg = msg + "\n\n"
860 p.write(msg)
860 p.write(msg)
861 if commitfiles:
861 if commitfiles:
862 parent = self.qparents(repo, n)
862 parent = self.qparents(repo, n)
863 chunks = patch.diff(repo, node1=parent, node2=n,
863 chunks = patch.diff(repo, node1=parent, node2=n,
864 match=match, opts=diffopts)
864 match=match, opts=diffopts)
865 for chunk in chunks:
865 for chunk in chunks:
866 p.write(chunk)
866 p.write(chunk)
867 p.close()
867 p.close()
868 wlock.release()
868 wlock.release()
869 wlock = None
869 wlock = None
870 r = self.qrepo()
870 r = self.qrepo()
871 if r:
871 if r:
872 r.add([patchfn])
872 r.add([patchfn])
873 except:
873 except:
874 repo.rollback()
874 repo.rollback()
875 raise
875 raise
876 except Exception:
876 except Exception:
877 patchpath = self.join(patchfn)
877 patchpath = self.join(patchfn)
878 try:
878 try:
879 os.unlink(patchpath)
879 os.unlink(patchpath)
880 except:
880 except:
881 self.ui.warn(_('error unlinking %s\n') % patchpath)
881 self.ui.warn(_('error unlinking %s\n') % patchpath)
882 raise
882 raise
883 self.removeundo(repo)
883 self.removeundo(repo)
884 finally:
884 finally:
885 release(wlock)
885 release(wlock)
886
886
887 def strip(self, repo, rev, update=True, backup="all", force=None):
887 def strip(self, repo, rev, update=True, backup="all", force=None):
888 wlock = lock = None
888 wlock = lock = None
889 try:
889 try:
890 wlock = repo.wlock()
890 wlock = repo.wlock()
891 lock = repo.lock()
891 lock = repo.lock()
892
892
893 if update:
893 if update:
894 self.check_localchanges(repo, force=force, refresh=False)
894 self.check_localchanges(repo, force=force, refresh=False)
895 urev = self.qparents(repo, rev)
895 urev = self.qparents(repo, rev)
896 hg.clean(repo, urev)
896 hg.clean(repo, urev)
897 repo.dirstate.write()
897 repo.dirstate.write()
898
898
899 self.removeundo(repo)
899 self.removeundo(repo)
900 repair.strip(self.ui, repo, rev, backup)
900 repair.strip(self.ui, repo, rev, backup)
901 # strip may have unbundled a set of backed up revisions after
901 # strip may have unbundled a set of backed up revisions after
902 # the actual strip
902 # the actual strip
903 self.removeundo(repo)
903 self.removeundo(repo)
904 finally:
904 finally:
905 release(lock, wlock)
905 release(lock, wlock)
906
906
907 def isapplied(self, patch):
907 def isapplied(self, patch):
908 """returns (index, rev, patch)"""
908 """returns (index, rev, patch)"""
909 for i, a in enumerate(self.applied):
909 for i, a in enumerate(self.applied):
910 if a.name == patch:
910 if a.name == patch:
911 return (i, a.rev, a.name)
911 return (i, a.rev, a.name)
912 return None
912 return None
913
913
914 # if the exact patch name does not exist, we try a few
914 # if the exact patch name does not exist, we try a few
915 # variations. If strict is passed, we try only #1
915 # variations. If strict is passed, we try only #1
916 #
916 #
917 # 1) a number to indicate an offset in the series file
917 # 1) a number to indicate an offset in the series file
918 # 2) a unique substring of the patch name was given
918 # 2) a unique substring of the patch name was given
919 # 3) patchname[-+]num to indicate an offset in the series file
919 # 3) patchname[-+]num to indicate an offset in the series file
920 def lookup(self, patch, strict=False):
920 def lookup(self, patch, strict=False):
921 patch = patch and str(patch)
921 patch = patch and str(patch)
922
922
923 def partial_name(s):
923 def partial_name(s):
924 if s in self.series:
924 if s in self.series:
925 return s
925 return s
926 matches = [x for x in self.series if s in x]
926 matches = [x for x in self.series if s in x]
927 if len(matches) > 1:
927 if len(matches) > 1:
928 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
928 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
929 for m in matches:
929 for m in matches:
930 self.ui.warn(' %s\n' % m)
930 self.ui.warn(' %s\n' % m)
931 return None
931 return None
932 if matches:
932 if matches:
933 return matches[0]
933 return matches[0]
934 if len(self.series) > 0 and len(self.applied) > 0:
934 if len(self.series) > 0 and len(self.applied) > 0:
935 if s == 'qtip':
935 if s == 'qtip':
936 return self.series[self.series_end(True)-1]
936 return self.series[self.series_end(True)-1]
937 if s == 'qbase':
937 if s == 'qbase':
938 return self.series[0]
938 return self.series[0]
939 return None
939 return None
940
940
941 if patch is None:
941 if patch is None:
942 return None
942 return None
943 if patch in self.series:
943 if patch in self.series:
944 return patch
944 return patch
945
945
946 if not os.path.isfile(self.join(patch)):
946 if not os.path.isfile(self.join(patch)):
947 try:
947 try:
948 sno = int(patch)
948 sno = int(patch)
949 except (ValueError, OverflowError):
949 except (ValueError, OverflowError):
950 pass
950 pass
951 else:
951 else:
952 if -len(self.series) <= sno < len(self.series):
952 if -len(self.series) <= sno < len(self.series):
953 return self.series[sno]
953 return self.series[sno]
954
954
955 if not strict:
955 if not strict:
956 res = partial_name(patch)
956 res = partial_name(patch)
957 if res:
957 if res:
958 return res
958 return res
959 minus = patch.rfind('-')
959 minus = patch.rfind('-')
960 if minus >= 0:
960 if minus >= 0:
961 res = partial_name(patch[:minus])
961 res = partial_name(patch[:minus])
962 if res:
962 if res:
963 i = self.series.index(res)
963 i = self.series.index(res)
964 try:
964 try:
965 off = int(patch[minus + 1:] or 1)
965 off = int(patch[minus + 1:] or 1)
966 except (ValueError, OverflowError):
966 except (ValueError, OverflowError):
967 pass
967 pass
968 else:
968 else:
969 if i - off >= 0:
969 if i - off >= 0:
970 return self.series[i - off]
970 return self.series[i - off]
971 plus = patch.rfind('+')
971 plus = patch.rfind('+')
972 if plus >= 0:
972 if plus >= 0:
973 res = partial_name(patch[:plus])
973 res = partial_name(patch[:plus])
974 if res:
974 if res:
975 i = self.series.index(res)
975 i = self.series.index(res)
976 try:
976 try:
977 off = int(patch[plus + 1:] or 1)
977 off = int(patch[plus + 1:] or 1)
978 except (ValueError, OverflowError):
978 except (ValueError, OverflowError):
979 pass
979 pass
980 else:
980 else:
981 if i + off < len(self.series):
981 if i + off < len(self.series):
982 return self.series[i + off]
982 return self.series[i + off]
983 raise util.Abort(_("patch %s not in series") % patch)
983 raise util.Abort(_("patch %s not in series") % patch)
984
984
985 def push(self, repo, patch=None, force=False, list=False,
985 def push(self, repo, patch=None, force=False, list=False,
986 mergeq=None, all=False):
986 mergeq=None, all=False):
987 diffopts = self.diffopts()
987 diffopts = self.diffopts()
988 wlock = repo.wlock()
988 wlock = repo.wlock()
989 try:
989 try:
990 heads = []
990 heads = []
991 for b, ls in repo.branchmap().iteritems():
991 for b, ls in repo.branchmap().iteritems():
992 heads += ls
992 heads += ls
993 if not heads:
993 if not heads:
994 heads = [nullid]
994 heads = [nullid]
995 if repo.dirstate.parents()[0] not in heads:
995 if repo.dirstate.parents()[0] not in heads:
996 self.ui.status(_("(working directory not at a head)\n"))
996 self.ui.status(_("(working directory not at a head)\n"))
997
997
998 if not self.series:
998 if not self.series:
999 self.ui.warn(_('no patches in series\n'))
999 self.ui.warn(_('no patches in series\n'))
1000 return 0
1000 return 0
1001
1001
1002 patch = self.lookup(patch)
1002 patch = self.lookup(patch)
1003 # Suppose our series file is: A B C and the current 'top'
1003 # Suppose our series file is: A B C and the current 'top'
1004 # patch is B. qpush C should be performed (moving forward)
1004 # patch is B. qpush C should be performed (moving forward)
1005 # qpush B is a NOP (no change) qpush A is an error (can't
1005 # qpush B is a NOP (no change) qpush A is an error (can't
1006 # go backwards with qpush)
1006 # go backwards with qpush)
1007 if patch:
1007 if patch:
1008 info = self.isapplied(patch)
1008 info = self.isapplied(patch)
1009 if info:
1009 if info:
1010 if info[0] < len(self.applied) - 1:
1010 if info[0] < len(self.applied) - 1:
1011 raise util.Abort(
1011 raise util.Abort(
1012 _("cannot push to a previous patch: %s") % patch)
1012 _("cannot push to a previous patch: %s") % patch)
1013 self.ui.warn(
1013 self.ui.warn(
1014 _('qpush: %s is already at the top\n') % patch)
1014 _('qpush: %s is already at the top\n') % patch)
1015 return
1015 return
1016 pushable, reason = self.pushable(patch)
1016 pushable, reason = self.pushable(patch)
1017 if not pushable:
1017 if not pushable:
1018 if reason:
1018 if reason:
1019 reason = _('guarded by %r') % reason
1019 reason = _('guarded by %r') % reason
1020 else:
1020 else:
1021 reason = _('no matching guards')
1021 reason = _('no matching guards')
1022 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1022 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1023 return 1
1023 return 1
1024 elif all:
1024 elif all:
1025 patch = self.series[-1]
1025 patch = self.series[-1]
1026 if self.isapplied(patch):
1026 if self.isapplied(patch):
1027 self.ui.warn(_('all patches are currently applied\n'))
1027 self.ui.warn(_('all patches are currently applied\n'))
1028 return 0
1028 return 0
1029
1029
1030 # Following the above example, starting at 'top' of B:
1030 # Following the above example, starting at 'top' of B:
1031 # qpush should be performed (pushes C), but a subsequent
1031 # qpush should be performed (pushes C), but a subsequent
1032 # qpush without an argument is an error (nothing to
1032 # qpush without an argument is an error (nothing to
1033 # apply). This allows a loop of "...while hg qpush..." to
1033 # apply). This allows a loop of "...while hg qpush..." to
1034 # work as it detects an error when done
1034 # work as it detects an error when done
1035 start = self.series_end()
1035 start = self.series_end()
1036 if start == len(self.series):
1036 if start == len(self.series):
1037 self.ui.warn(_('patch series already fully applied\n'))
1037 self.ui.warn(_('patch series already fully applied\n'))
1038 return 1
1038 return 1
1039 if not force:
1039 if not force:
1040 self.check_localchanges(repo)
1040 self.check_localchanges(repo)
1041
1041
1042 self.applied_dirty = 1
1042 self.applied_dirty = 1
1043 if start > 0:
1043 if start > 0:
1044 self.check_toppatch(repo)
1044 self.check_toppatch(repo)
1045 if not patch:
1045 if not patch:
1046 patch = self.series[start]
1046 patch = self.series[start]
1047 end = start + 1
1047 end = start + 1
1048 else:
1048 else:
1049 end = self.series.index(patch, start) + 1
1049 end = self.series.index(patch, start) + 1
1050
1050
1051 s = self.series[start:end]
1051 s = self.series[start:end]
1052 all_files = {}
1052 all_files = {}
1053 try:
1053 try:
1054 if mergeq:
1054 if mergeq:
1055 ret = self.mergepatch(repo, mergeq, s, diffopts)
1055 ret = self.mergepatch(repo, mergeq, s, diffopts)
1056 else:
1056 else:
1057 ret = self.apply(repo, s, list, all_files=all_files)
1057 ret = self.apply(repo, s, list, all_files=all_files)
1058 except:
1058 except:
1059 self.ui.warn(_('cleaning up working directory...'))
1059 self.ui.warn(_('cleaning up working directory...'))
1060 node = repo.dirstate.parents()[0]
1060 node = repo.dirstate.parents()[0]
1061 hg.revert(repo, node, None)
1061 hg.revert(repo, node, None)
1062 unknown = repo.status(unknown=True)[4]
1062 unknown = repo.status(unknown=True)[4]
1063 # only remove unknown files that we know we touched or
1063 # only remove unknown files that we know we touched or
1064 # created while patching
1064 # created while patching
1065 for f in unknown:
1065 for f in unknown:
1066 if f in all_files:
1066 if f in all_files:
1067 util.unlink(repo.wjoin(f))
1067 util.unlink(repo.wjoin(f))
1068 self.ui.warn(_('done\n'))
1068 self.ui.warn(_('done\n'))
1069 raise
1069 raise
1070
1070
1071 if not self.applied:
1071 if not self.applied:
1072 return ret[0]
1072 return ret[0]
1073 top = self.applied[-1].name
1073 top = self.applied[-1].name
1074 if ret[0] and ret[0] > 1:
1074 if ret[0] and ret[0] > 1:
1075 msg = _("errors during apply, please fix and refresh %s\n")
1075 msg = _("errors during apply, please fix and refresh %s\n")
1076 self.ui.write(msg % top)
1076 self.ui.write(msg % top)
1077 else:
1077 else:
1078 self.ui.write(_("now at: %s\n") % top)
1078 self.ui.write(_("now at: %s\n") % top)
1079 return ret[0]
1079 return ret[0]
1080
1080
1081 finally:
1081 finally:
1082 wlock.release()
1082 wlock.release()
1083
1083
1084 def pop(self, repo, patch=None, force=False, update=True, all=False):
1084 def pop(self, repo, patch=None, force=False, update=True, all=False):
1085 def getfile(f, rev, flags):
1085 def getfile(f, rev, flags):
1086 t = repo.file(f).read(rev)
1086 t = repo.file(f).read(rev)
1087 repo.wwrite(f, t, flags)
1087 repo.wwrite(f, t, flags)
1088
1088
1089 wlock = repo.wlock()
1089 wlock = repo.wlock()
1090 try:
1090 try:
1091 if patch:
1091 if patch:
1092 # index, rev, patch
1092 # index, rev, patch
1093 info = self.isapplied(patch)
1093 info = self.isapplied(patch)
1094 if not info:
1094 if not info:
1095 patch = self.lookup(patch)
1095 patch = self.lookup(patch)
1096 info = self.isapplied(patch)
1096 info = self.isapplied(patch)
1097 if not info:
1097 if not info:
1098 raise util.Abort(_("patch %s is not applied") % patch)
1098 raise util.Abort(_("patch %s is not applied") % patch)
1099
1099
1100 if len(self.applied) == 0:
1100 if len(self.applied) == 0:
1101 # Allow qpop -a to work repeatedly,
1101 # Allow qpop -a to work repeatedly,
1102 # but not qpop without an argument
1102 # but not qpop without an argument
1103 self.ui.warn(_("no patches applied\n"))
1103 self.ui.warn(_("no patches applied\n"))
1104 return not all
1104 return not all
1105
1105
1106 if all:
1106 if all:
1107 start = 0
1107 start = 0
1108 elif patch:
1108 elif patch:
1109 start = info[0] + 1
1109 start = info[0] + 1
1110 else:
1110 else:
1111 start = len(self.applied) - 1
1111 start = len(self.applied) - 1
1112
1112
1113 if start >= len(self.applied):
1113 if start >= len(self.applied):
1114 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1114 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1115 return
1115 return
1116
1116
1117 if not update:
1117 if not update:
1118 parents = repo.dirstate.parents()
1118 parents = repo.dirstate.parents()
1119 rr = [bin(x.rev) for x in self.applied]
1119 rr = [bin(x.rev) for x in self.applied]
1120 for p in parents:
1120 for p in parents:
1121 if p in rr:
1121 if p in rr:
1122 self.ui.warn(_("qpop: forcing dirstate update\n"))
1122 self.ui.warn(_("qpop: forcing dirstate update\n"))
1123 update = True
1123 update = True
1124 else:
1124 else:
1125 parents = [p.hex() for p in repo[None].parents()]
1125 parents = [p.hex() for p in repo[None].parents()]
1126 needupdate = False
1126 needupdate = False
1127 for entry in self.applied[start:]:
1127 for entry in self.applied[start:]:
1128 if entry.rev in parents:
1128 if entry.rev in parents:
1129 needupdate = True
1129 needupdate = True
1130 break
1130 break
1131 update = needupdate
1131 update = needupdate
1132
1132
1133 if not force and update:
1133 if not force and update:
1134 self.check_localchanges(repo)
1134 self.check_localchanges(repo)
1135
1135
1136 self.applied_dirty = 1
1136 self.applied_dirty = 1
1137 end = len(self.applied)
1137 end = len(self.applied)
1138 rev = bin(self.applied[start].rev)
1138 rev = bin(self.applied[start].rev)
1139 if update:
1139 if update:
1140 top = self.check_toppatch(repo)[0]
1140 top = self.check_toppatch(repo)[0]
1141
1141
1142 try:
1142 try:
1143 heads = repo.changelog.heads(rev)
1143 heads = repo.changelog.heads(rev)
1144 except error.LookupError:
1144 except error.LookupError:
1145 node = short(rev)
1145 node = short(rev)
1146 raise util.Abort(_('trying to pop unknown node %s') % node)
1146 raise util.Abort(_('trying to pop unknown node %s') % node)
1147
1147
1148 if heads != [bin(self.applied[-1].rev)]:
1148 if heads != [bin(self.applied[-1].rev)]:
1149 raise util.Abort(_("popping would remove a revision not "
1149 raise util.Abort(_("popping would remove a revision not "
1150 "managed by this patch queue"))
1150 "managed by this patch queue"))
1151
1151
1152 # we know there are no local changes, so we can make a simplified
1152 # we know there are no local changes, so we can make a simplified
1153 # form of hg.update.
1153 # form of hg.update.
1154 if update:
1154 if update:
1155 qp = self.qparents(repo, rev)
1155 qp = self.qparents(repo, rev)
1156 changes = repo.changelog.read(qp)
1156 changes = repo.changelog.read(qp)
1157 mmap = repo.manifest.read(changes[0])
1157 mmap = repo.manifest.read(changes[0])
1158 m, a, r, d = repo.status(qp, top)[:4]
1158 m, a, r, d = repo.status(qp, top)[:4]
1159 if d:
1159 if d:
1160 raise util.Abort(_("deletions found between repo revs"))
1160 raise util.Abort(_("deletions found between repo revs"))
1161 for f in a:
1161 for f in a:
1162 try:
1162 try:
1163 os.unlink(repo.wjoin(f))
1163 os.unlink(repo.wjoin(f))
1164 except OSError, e:
1164 except OSError, e:
1165 if e.errno != errno.ENOENT:
1165 if e.errno != errno.ENOENT:
1166 raise
1166 raise
1167 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1167 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1168 except: pass
1168 except: pass
1169 repo.dirstate.forget(f)
1169 repo.dirstate.forget(f)
1170 for f in m:
1170 for f in m:
1171 getfile(f, mmap[f], mmap.flags(f))
1171 getfile(f, mmap[f], mmap.flags(f))
1172 for f in r:
1172 for f in r:
1173 getfile(f, mmap[f], mmap.flags(f))
1173 getfile(f, mmap[f], mmap.flags(f))
1174 for f in m + r:
1174 for f in m + r:
1175 repo.dirstate.normal(f)
1175 repo.dirstate.normal(f)
1176 repo.dirstate.setparents(qp, nullid)
1176 repo.dirstate.setparents(qp, nullid)
1177 for patch in reversed(self.applied[start:end]):
1177 for patch in reversed(self.applied[start:end]):
1178 self.ui.status(_("popping %s\n") % patch.name)
1178 self.ui.status(_("popping %s\n") % patch.name)
1179 del self.applied[start:end]
1179 del self.applied[start:end]
1180 self.strip(repo, rev, update=False, backup='strip')
1180 self.strip(repo, rev, update=False, backup='strip')
1181 if len(self.applied):
1181 if len(self.applied):
1182 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1182 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1183 else:
1183 else:
1184 self.ui.write(_("patch queue now empty\n"))
1184 self.ui.write(_("patch queue now empty\n"))
1185 finally:
1185 finally:
1186 wlock.release()
1186 wlock.release()
1187
1187
1188 def diff(self, repo, pats, opts):
1188 def diff(self, repo, pats, opts):
1189 top, patch = self.check_toppatch(repo)
1189 top, patch = self.check_toppatch(repo)
1190 if not top:
1190 if not top:
1191 self.ui.write(_("no patches applied\n"))
1191 self.ui.write(_("no patches applied\n"))
1192 return
1192 return
1193 qp = self.qparents(repo, top)
1193 qp = self.qparents(repo, top)
1194 if opts.get('reverse'):
1194 if opts.get('reverse'):
1195 node1, node2 = None, qp
1195 node1, node2 = None, qp
1196 else:
1196 else:
1197 node1, node2 = qp, None
1197 node1, node2 = qp, None
1198 diffopts = self.diffopts(opts, patch)
1198 diffopts = self.diffopts(opts, patch)
1199 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1199 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1200
1200
1201 def refresh(self, repo, pats=None, **opts):
1201 def refresh(self, repo, pats=None, **opts):
1202 if len(self.applied) == 0:
1202 if len(self.applied) == 0:
1203 self.ui.write(_("no patches applied\n"))
1203 self.ui.write(_("no patches applied\n"))
1204 return 1
1204 return 1
1205 msg = opts.get('msg', '').rstrip()
1205 msg = opts.get('msg', '').rstrip()
1206 newuser = opts.get('user')
1206 newuser = opts.get('user')
1207 newdate = opts.get('date')
1207 newdate = opts.get('date')
1208 if newdate:
1208 if newdate:
1209 newdate = '%d %d' % util.parsedate(newdate)
1209 newdate = '%d %d' % util.parsedate(newdate)
1210 wlock = repo.wlock()
1210 wlock = repo.wlock()
1211
1211
1212 try:
1212 try:
1213 self.check_toppatch(repo)
1213 self.check_toppatch(repo)
1214 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1214 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1215 top = bin(top)
1215 top = bin(top)
1216 if repo.changelog.heads(top) != [top]:
1216 if repo.changelog.heads(top) != [top]:
1217 raise util.Abort(_("cannot refresh a revision with children"))
1217 raise util.Abort(_("cannot refresh a revision with children"))
1218
1218
1219 cparents = repo.changelog.parents(top)
1219 cparents = repo.changelog.parents(top)
1220 patchparent = self.qparents(repo, top)
1220 patchparent = self.qparents(repo, top)
1221 ph = patchheader(self.join(patchfn))
1221 ph = patchheader(self.join(patchfn))
1222 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1222 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1223 if msg:
1223 if msg:
1224 ph.setmessage(msg)
1224 ph.setmessage(msg)
1225 if newuser:
1225 if newuser:
1226 ph.setuser(newuser)
1226 ph.setuser(newuser)
1227 if newdate:
1227 if newdate:
1228 ph.setdate(newdate)
1228 ph.setdate(newdate)
1229
1229
1230 # only commit new patch when write is complete
1230 # only commit new patch when write is complete
1231 patchf = self.opener(patchfn, 'w', atomictemp=True)
1231 patchf = self.opener(patchfn, 'w', atomictemp=True)
1232
1232
1233 comments = str(ph)
1233 comments = str(ph)
1234 if comments:
1234 if comments:
1235 patchf.write(comments)
1235 patchf.write(comments)
1236
1236
1237 # update the dirstate in place, strip off the qtip commit
1237 # update the dirstate in place, strip off the qtip commit
1238 # and then commit.
1238 # and then commit.
1239 #
1239 #
1240 # this should really read:
1240 # this should really read:
1241 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1241 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1242 # but we do it backwards to take advantage of manifest/chlog
1242 # but we do it backwards to take advantage of manifest/chlog
1243 # caching against the next repo.status call
1243 # caching against the next repo.status call
1244 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1244 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1245 changes = repo.changelog.read(top)
1245 changes = repo.changelog.read(top)
1246 man = repo.manifest.read(changes[0])
1246 man = repo.manifest.read(changes[0])
1247 aaa = aa[:]
1247 aaa = aa[:]
1248 matchfn = cmdutil.match(repo, pats, opts)
1248 matchfn = cmdutil.match(repo, pats, opts)
1249 # in short mode, we only diff the files included in the
1249 # in short mode, we only diff the files included in the
1250 # patch already plus specified files
1250 # patch already plus specified files
1251 if opts.get('short'):
1251 if opts.get('short'):
1252 # if amending a patch, we start with existing
1252 # if amending a patch, we start with existing
1253 # files plus specified files - unfiltered
1253 # files plus specified files - unfiltered
1254 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1254 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1255 # filter with inc/exl options
1255 # filter with inc/exl options
1256 matchfn = cmdutil.match(repo, opts=opts)
1256 matchfn = cmdutil.match(repo, opts=opts)
1257 else:
1257 else:
1258 match = cmdutil.matchall(repo)
1258 match = cmdutil.matchall(repo)
1259 m, a, r, d = repo.status(match=match)[:4]
1259 m, a, r, d = repo.status(match=match)[:4]
1260
1260
1261 # we might end up with files that were added between
1261 # we might end up with files that were added between
1262 # qtip and the dirstate parent, but then changed in the
1262 # qtip and the dirstate parent, but then changed in the
1263 # local dirstate. in this case, we want them to only
1263 # local dirstate. in this case, we want them to only
1264 # show up in the added section
1264 # show up in the added section
1265 for x in m:
1265 for x in m:
1266 if x not in aa:
1266 if x not in aa:
1267 mm.append(x)
1267 mm.append(x)
1268 # we might end up with files added by the local dirstate that
1268 # we might end up with files added by the local dirstate that
1269 # were deleted by the patch. In this case, they should only
1269 # were deleted by the patch. In this case, they should only
1270 # show up in the changed section.
1270 # show up in the changed section.
1271 for x in a:
1271 for x in a:
1272 if x in dd:
1272 if x in dd:
1273 del dd[dd.index(x)]
1273 del dd[dd.index(x)]
1274 mm.append(x)
1274 mm.append(x)
1275 else:
1275 else:
1276 aa.append(x)
1276 aa.append(x)
1277 # make sure any files deleted in the local dirstate
1277 # make sure any files deleted in the local dirstate
1278 # are not in the add or change column of the patch
1278 # are not in the add or change column of the patch
1279 forget = []
1279 forget = []
1280 for x in d + r:
1280 for x in d + r:
1281 if x in aa:
1281 if x in aa:
1282 del aa[aa.index(x)]
1282 del aa[aa.index(x)]
1283 forget.append(x)
1283 forget.append(x)
1284 continue
1284 continue
1285 elif x in mm:
1285 elif x in mm:
1286 del mm[mm.index(x)]
1286 del mm[mm.index(x)]
1287 dd.append(x)
1287 dd.append(x)
1288
1288
1289 m = list(set(mm))
1289 m = list(set(mm))
1290 r = list(set(dd))
1290 r = list(set(dd))
1291 a = list(set(aa))
1291 a = list(set(aa))
1292 c = [filter(matchfn, l) for l in (m, a, r)]
1292 c = [filter(matchfn, l) for l in (m, a, r)]
1293 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1293 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1294 chunks = patch.diff(repo, patchparent, match=match,
1294 chunks = patch.diff(repo, patchparent, match=match,
1295 changes=c, opts=diffopts)
1295 changes=c, opts=diffopts)
1296 for chunk in chunks:
1296 for chunk in chunks:
1297 patchf.write(chunk)
1297 patchf.write(chunk)
1298
1298
1299 try:
1299 try:
1300 if diffopts.git or diffopts.upgrade:
1300 if diffopts.git or diffopts.upgrade:
1301 copies = {}
1301 copies = {}
1302 for dst in a:
1302 for dst in a:
1303 src = repo.dirstate.copied(dst)
1303 src = repo.dirstate.copied(dst)
1304 # during qfold, the source file for copies may
1304 # during qfold, the source file for copies may
1305 # be removed. Treat this as a simple add.
1305 # be removed. Treat this as a simple add.
1306 if src is not None and src in repo.dirstate:
1306 if src is not None and src in repo.dirstate:
1307 copies.setdefault(src, []).append(dst)
1307 copies.setdefault(src, []).append(dst)
1308 repo.dirstate.add(dst)
1308 repo.dirstate.add(dst)
1309 # remember the copies between patchparent and qtip
1309 # remember the copies between patchparent and qtip
1310 for dst in aaa:
1310 for dst in aaa:
1311 f = repo.file(dst)
1311 f = repo.file(dst)
1312 src = f.renamed(man[dst])
1312 src = f.renamed(man[dst])
1313 if src:
1313 if src:
1314 copies.setdefault(src[0], []).extend(
1314 copies.setdefault(src[0], []).extend(
1315 copies.get(dst, []))
1315 copies.get(dst, []))
1316 if dst in a:
1316 if dst in a:
1317 copies[src[0]].append(dst)
1317 copies[src[0]].append(dst)
1318 # we can't copy a file created by the patch itself
1318 # we can't copy a file created by the patch itself
1319 if dst in copies:
1319 if dst in copies:
1320 del copies[dst]
1320 del copies[dst]
1321 for src, dsts in copies.iteritems():
1321 for src, dsts in copies.iteritems():
1322 for dst in dsts:
1322 for dst in dsts:
1323 repo.dirstate.copy(src, dst)
1323 repo.dirstate.copy(src, dst)
1324 else:
1324 else:
1325 for dst in a:
1325 for dst in a:
1326 repo.dirstate.add(dst)
1326 repo.dirstate.add(dst)
1327 # Drop useless copy information
1327 # Drop useless copy information
1328 for f in list(repo.dirstate.copies()):
1328 for f in list(repo.dirstate.copies()):
1329 repo.dirstate.copy(None, f)
1329 repo.dirstate.copy(None, f)
1330 for f in r:
1330 for f in r:
1331 repo.dirstate.remove(f)
1331 repo.dirstate.remove(f)
1332 # if the patch excludes a modified file, mark that
1332 # if the patch excludes a modified file, mark that
1333 # file with mtime=0 so status can see it.
1333 # file with mtime=0 so status can see it.
1334 mm = []
1334 mm = []
1335 for i in xrange(len(m)-1, -1, -1):
1335 for i in xrange(len(m)-1, -1, -1):
1336 if not matchfn(m[i]):
1336 if not matchfn(m[i]):
1337 mm.append(m[i])
1337 mm.append(m[i])
1338 del m[i]
1338 del m[i]
1339 for f in m:
1339 for f in m:
1340 repo.dirstate.normal(f)
1340 repo.dirstate.normal(f)
1341 for f in mm:
1341 for f in mm:
1342 repo.dirstate.normallookup(f)
1342 repo.dirstate.normallookup(f)
1343 for f in forget:
1343 for f in forget:
1344 repo.dirstate.forget(f)
1344 repo.dirstate.forget(f)
1345
1345
1346 if not msg:
1346 if not msg:
1347 if not ph.message:
1347 if not ph.message:
1348 message = "[mq]: %s\n" % patchfn
1348 message = "[mq]: %s\n" % patchfn
1349 else:
1349 else:
1350 message = "\n".join(ph.message)
1350 message = "\n".join(ph.message)
1351 else:
1351 else:
1352 message = msg
1352 message = msg
1353
1353
1354 user = ph.user or changes[1]
1354 user = ph.user or changes[1]
1355
1355
1356 # assumes strip can roll itself back if interrupted
1356 # assumes strip can roll itself back if interrupted
1357 repo.dirstate.setparents(*cparents)
1357 repo.dirstate.setparents(*cparents)
1358 self.applied.pop()
1358 self.applied.pop()
1359 self.applied_dirty = 1
1359 self.applied_dirty = 1
1360 self.strip(repo, top, update=False,
1360 self.strip(repo, top, update=False,
1361 backup='strip')
1361 backup='strip')
1362 except:
1362 except:
1363 repo.dirstate.invalidate()
1363 repo.dirstate.invalidate()
1364 raise
1364 raise
1365
1365
1366 try:
1366 try:
1367 # might be nice to attempt to roll back strip after this
1367 # might be nice to attempt to roll back strip after this
1368 patchf.rename()
1368 patchf.rename()
1369 n = repo.commit(message, user, ph.date, match=match,
1369 n = repo.commit(message, user, ph.date, match=match,
1370 force=True)
1370 force=True)
1371 self.applied.append(statusentry(hex(n), patchfn))
1371 self.applied.append(statusentry(hex(n), patchfn))
1372 except:
1372 except:
1373 ctx = repo[cparents[0]]
1373 ctx = repo[cparents[0]]
1374 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1374 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1375 self.save_dirty()
1375 self.save_dirty()
1376 self.ui.warn(_('refresh interrupted while patch was popped! '
1376 self.ui.warn(_('refresh interrupted while patch was popped! '
1377 '(revert --all, qpush to recover)\n'))
1377 '(revert --all, qpush to recover)\n'))
1378 raise
1378 raise
1379 finally:
1379 finally:
1380 wlock.release()
1380 wlock.release()
1381 self.removeundo(repo)
1381 self.removeundo(repo)
1382
1382
1383 def init(self, repo, create=False):
1383 def init(self, repo, create=False):
1384 if not create and os.path.isdir(self.path):
1384 if not create and os.path.isdir(self.path):
1385 raise util.Abort(_("patch queue directory already exists"))
1385 raise util.Abort(_("patch queue directory already exists"))
1386 try:
1386 try:
1387 os.mkdir(self.path)
1387 os.mkdir(self.path)
1388 except OSError, inst:
1388 except OSError, inst:
1389 if inst.errno != errno.EEXIST or not create:
1389 if inst.errno != errno.EEXIST or not create:
1390 raise
1390 raise
1391 if create:
1391 if create:
1392 return self.qrepo(create=True)
1392 return self.qrepo(create=True)
1393
1393
1394 def unapplied(self, repo, patch=None):
1394 def unapplied(self, repo, patch=None):
1395 if patch and patch not in self.series:
1395 if patch and patch not in self.series:
1396 raise util.Abort(_("patch %s is not in series file") % patch)
1396 raise util.Abort(_("patch %s is not in series file") % patch)
1397 if not patch:
1397 if not patch:
1398 start = self.series_end()
1398 start = self.series_end()
1399 else:
1399 else:
1400 start = self.series.index(patch) + 1
1400 start = self.series.index(patch) + 1
1401 unapplied = []
1401 unapplied = []
1402 for i in xrange(start, len(self.series)):
1402 for i in xrange(start, len(self.series)):
1403 pushable, reason = self.pushable(i)
1403 pushable, reason = self.pushable(i)
1404 if pushable:
1404 if pushable:
1405 unapplied.append((i, self.series[i]))
1405 unapplied.append((i, self.series[i]))
1406 self.explain_pushable(i)
1406 self.explain_pushable(i)
1407 return unapplied
1407 return unapplied
1408
1408
1409 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1409 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1410 summary=False):
1410 summary=False):
1411 def displayname(pfx, patchname):
1411 def displayname(pfx, patchname):
1412 if summary:
1412 if summary:
1413 ph = patchheader(self.join(patchname))
1413 ph = patchheader(self.join(patchname))
1414 msg = ph.message and ph.message[0] or ''
1414 msg = ph.message and ph.message[0] or ''
1415 if self.ui.interactive():
1415 if self.ui.interactive():
1416 width = util.termwidth() - len(pfx) - len(patchname) - 2
1416 width = util.termwidth() - len(pfx) - len(patchname) - 2
1417 if width > 0:
1417 if width > 0:
1418 msg = util.ellipsis(msg, width)
1418 msg = util.ellipsis(msg, width)
1419 else:
1419 else:
1420 msg = ''
1420 msg = ''
1421 msg = "%s%s: %s" % (pfx, patchname, msg)
1421 msg = "%s%s: %s" % (pfx, patchname, msg)
1422 else:
1422 else:
1423 msg = pfx + patchname
1423 msg = pfx + patchname
1424 self.ui.write(msg + '\n')
1424 self.ui.write(msg + '\n')
1425
1425
1426 applied = set([p.name for p in self.applied])
1426 applied = set([p.name for p in self.applied])
1427 if length is None:
1427 if length is None:
1428 length = len(self.series) - start
1428 length = len(self.series) - start
1429 if not missing:
1429 if not missing:
1430 if self.ui.verbose:
1430 if self.ui.verbose:
1431 idxwidth = len(str(start + length - 1))
1431 idxwidth = len(str(start + length - 1))
1432 for i in xrange(start, start + length):
1432 for i in xrange(start, start + length):
1433 patch = self.series[i]
1433 patch = self.series[i]
1434 if patch in applied:
1434 if patch in applied:
1435 stat = 'A'
1435 stat = 'A'
1436 elif self.pushable(i)[0]:
1436 elif self.pushable(i)[0]:
1437 stat = 'U'
1437 stat = 'U'
1438 else:
1438 else:
1439 stat = 'G'
1439 stat = 'G'
1440 pfx = ''
1440 pfx = ''
1441 if self.ui.verbose:
1441 if self.ui.verbose:
1442 pfx = '%*d %s ' % (idxwidth, i, stat)
1442 pfx = '%*d %s ' % (idxwidth, i, stat)
1443 elif status and status != stat:
1443 elif status and status != stat:
1444 continue
1444 continue
1445 displayname(pfx, patch)
1445 displayname(pfx, patch)
1446 else:
1446 else:
1447 msng_list = []
1447 msng_list = []
1448 for root, dirs, files in os.walk(self.path):
1448 for root, dirs, files in os.walk(self.path):
1449 d = root[len(self.path) + 1:]
1449 d = root[len(self.path) + 1:]
1450 for f in files:
1450 for f in files:
1451 fl = os.path.join(d, f)
1451 fl = os.path.join(d, f)
1452 if (fl not in self.series and
1452 if (fl not in self.series and
1453 fl not in (self.status_path, self.series_path,
1453 fl not in (self.status_path, self.series_path,
1454 self.guards_path)
1454 self.guards_path)
1455 and not fl.startswith('.')):
1455 and not fl.startswith('.')):
1456 msng_list.append(fl)
1456 msng_list.append(fl)
1457 for x in sorted(msng_list):
1457 for x in sorted(msng_list):
1458 pfx = self.ui.verbose and ('D ') or ''
1458 pfx = self.ui.verbose and ('D ') or ''
1459 displayname(pfx, x)
1459 displayname(pfx, x)
1460
1460
1461 def issaveline(self, l):
1461 def issaveline(self, l):
1462 if l.name == '.hg.patches.save.line':
1462 if l.name == '.hg.patches.save.line':
1463 return True
1463 return True
1464
1464
1465 def qrepo(self, create=False):
1465 def qrepo(self, create=False):
1466 if create or os.path.isdir(self.join(".hg")):
1466 if create or os.path.isdir(self.join(".hg")):
1467 return hg.repository(self.ui, path=self.path, create=create)
1467 return hg.repository(self.ui, path=self.path, create=create)
1468
1468
1469 def restore(self, repo, rev, delete=None, qupdate=None):
1469 def restore(self, repo, rev, delete=None, qupdate=None):
1470 c = repo.changelog.read(rev)
1470 c = repo.changelog.read(rev)
1471 desc = c[4].strip()
1471 desc = c[4].strip()
1472 lines = desc.splitlines()
1472 lines = desc.splitlines()
1473 i = 0
1473 i = 0
1474 datastart = None
1474 datastart = None
1475 series = []
1475 series = []
1476 applied = []
1476 applied = []
1477 qpp = None
1477 qpp = None
1478 for i, line in enumerate(lines):
1478 for i, line in enumerate(lines):
1479 if line == 'Patch Data:':
1479 if line == 'Patch Data:':
1480 datastart = i + 1
1480 datastart = i + 1
1481 elif line.startswith('Dirstate:'):
1481 elif line.startswith('Dirstate:'):
1482 l = line.rstrip()
1482 l = line.rstrip()
1483 l = l[10:].split(' ')
1483 l = l[10:].split(' ')
1484 qpp = [bin(x) for x in l]
1484 qpp = [bin(x) for x in l]
1485 elif datastart != None:
1485 elif datastart != None:
1486 l = line.rstrip()
1486 l = line.rstrip()
1487 se = statusentry(l)
1487 se = statusentry(l)
1488 file_ = se.name
1488 file_ = se.name
1489 if se.rev:
1489 if se.rev:
1490 applied.append(se)
1490 applied.append(se)
1491 else:
1491 else:
1492 series.append(file_)
1492 series.append(file_)
1493 if datastart is None:
1493 if datastart is None:
1494 self.ui.warn(_("No saved patch data found\n"))
1494 self.ui.warn(_("No saved patch data found\n"))
1495 return 1
1495 return 1
1496 self.ui.warn(_("restoring status: %s\n") % lines[0])
1496 self.ui.warn(_("restoring status: %s\n") % lines[0])
1497 self.full_series = series
1497 self.full_series = series
1498 self.applied = applied
1498 self.applied = applied
1499 self.parse_series()
1499 self.parse_series()
1500 self.series_dirty = 1
1500 self.series_dirty = 1
1501 self.applied_dirty = 1
1501 self.applied_dirty = 1
1502 heads = repo.changelog.heads()
1502 heads = repo.changelog.heads()
1503 if delete:
1503 if delete:
1504 if rev not in heads:
1504 if rev not in heads:
1505 self.ui.warn(_("save entry has children, leaving it alone\n"))
1505 self.ui.warn(_("save entry has children, leaving it alone\n"))
1506 else:
1506 else:
1507 self.ui.warn(_("removing save entry %s\n") % short(rev))
1507 self.ui.warn(_("removing save entry %s\n") % short(rev))
1508 pp = repo.dirstate.parents()
1508 pp = repo.dirstate.parents()
1509 if rev in pp:
1509 if rev in pp:
1510 update = True
1510 update = True
1511 else:
1511 else:
1512 update = False
1512 update = False
1513 self.strip(repo, rev, update=update, backup='strip')
1513 self.strip(repo, rev, update=update, backup='strip')
1514 if qpp:
1514 if qpp:
1515 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1515 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1516 (short(qpp[0]), short(qpp[1])))
1516 (short(qpp[0]), short(qpp[1])))
1517 if qupdate:
1517 if qupdate:
1518 self.ui.status(_("queue directory updating\n"))
1518 self.ui.status(_("queue directory updating\n"))
1519 r = self.qrepo()
1519 r = self.qrepo()
1520 if not r:
1520 if not r:
1521 self.ui.warn(_("Unable to load queue repository\n"))
1521 self.ui.warn(_("Unable to load queue repository\n"))
1522 return 1
1522 return 1
1523 hg.clean(r, qpp[0])
1523 hg.clean(r, qpp[0])
1524
1524
1525 def save(self, repo, msg=None):
1525 def save(self, repo, msg=None):
1526 if len(self.applied) == 0:
1526 if len(self.applied) == 0:
1527 self.ui.warn(_("save: no patches applied, exiting\n"))
1527 self.ui.warn(_("save: no patches applied, exiting\n"))
1528 return 1
1528 return 1
1529 if self.issaveline(self.applied[-1]):
1529 if self.issaveline(self.applied[-1]):
1530 self.ui.warn(_("status is already saved\n"))
1530 self.ui.warn(_("status is already saved\n"))
1531 return 1
1531 return 1
1532
1532
1533 ar = [':' + x for x in self.full_series]
1533 ar = [':' + x for x in self.full_series]
1534 if not msg:
1534 if not msg:
1535 msg = _("hg patches saved state")
1535 msg = _("hg patches saved state")
1536 else:
1536 else:
1537 msg = "hg patches: " + msg.rstrip('\r\n')
1537 msg = "hg patches: " + msg.rstrip('\r\n')
1538 r = self.qrepo()
1538 r = self.qrepo()
1539 if r:
1539 if r:
1540 pp = r.dirstate.parents()
1540 pp = r.dirstate.parents()
1541 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1541 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1542 msg += "\n\nPatch Data:\n"
1542 msg += "\n\nPatch Data:\n"
1543 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1543 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1544 "\n".join(ar) + '\n' or "")
1544 "\n".join(ar) + '\n' or "")
1545 n = repo.commit(text, force=True)
1545 n = repo.commit(text, force=True)
1546 if not n:
1546 if not n:
1547 self.ui.warn(_("repo commit failed\n"))
1547 self.ui.warn(_("repo commit failed\n"))
1548 return 1
1548 return 1
1549 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1549 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1550 self.applied_dirty = 1
1550 self.applied_dirty = 1
1551 self.removeundo(repo)
1551 self.removeundo(repo)
1552
1552
1553 def full_series_end(self):
1553 def full_series_end(self):
1554 if len(self.applied) > 0:
1554 if len(self.applied) > 0:
1555 p = self.applied[-1].name
1555 p = self.applied[-1].name
1556 end = self.find_series(p)
1556 end = self.find_series(p)
1557 if end is None:
1557 if end is None:
1558 return len(self.full_series)
1558 return len(self.full_series)
1559 return end + 1
1559 return end + 1
1560 return 0
1560 return 0
1561
1561
1562 def series_end(self, all_patches=False):
1562 def series_end(self, all_patches=False):
1563 """If all_patches is False, return the index of the next pushable patch
1563 """If all_patches is False, return the index of the next pushable patch
1564 in the series, or the series length. If all_patches is True, return the
1564 in the series, or the series length. If all_patches is True, return the
1565 index of the first patch past the last applied one.
1565 index of the first patch past the last applied one.
1566 """
1566 """
1567 end = 0
1567 end = 0
1568 def next(start):
1568 def next(start):
1569 if all_patches:
1569 if all_patches:
1570 return start
1570 return start
1571 i = start
1571 i = start
1572 while i < len(self.series):
1572 while i < len(self.series):
1573 p, reason = self.pushable(i)
1573 p, reason = self.pushable(i)
1574 if p:
1574 if p:
1575 break
1575 break
1576 self.explain_pushable(i)
1576 self.explain_pushable(i)
1577 i += 1
1577 i += 1
1578 return i
1578 return i
1579 if len(self.applied) > 0:
1579 if len(self.applied) > 0:
1580 p = self.applied[-1].name
1580 p = self.applied[-1].name
1581 try:
1581 try:
1582 end = self.series.index(p)
1582 end = self.series.index(p)
1583 except ValueError:
1583 except ValueError:
1584 return 0
1584 return 0
1585 return next(end + 1)
1585 return next(end + 1)
1586 return next(end)
1586 return next(end)
1587
1587
1588 def appliedname(self, index):
1588 def appliedname(self, index):
1589 pname = self.applied[index].name
1589 pname = self.applied[index].name
1590 if not self.ui.verbose:
1590 if not self.ui.verbose:
1591 p = pname
1591 p = pname
1592 else:
1592 else:
1593 p = str(self.series.index(pname)) + " " + pname
1593 p = str(self.series.index(pname)) + " " + pname
1594 return p
1594 return p
1595
1595
1596 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1596 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1597 force=None, git=False):
1597 force=None, git=False):
1598 def checkseries(patchname):
1598 def checkseries(patchname):
1599 if patchname in self.series:
1599 if patchname in self.series:
1600 raise util.Abort(_('patch %s is already in the series file')
1600 raise util.Abort(_('patch %s is already in the series file')
1601 % patchname)
1601 % patchname)
1602 def checkfile(patchname):
1602 def checkfile(patchname):
1603 if not force and os.path.exists(self.join(patchname)):
1603 if not force and os.path.exists(self.join(patchname)):
1604 raise util.Abort(_('patch "%s" already exists')
1604 raise util.Abort(_('patch "%s" already exists')
1605 % patchname)
1605 % patchname)
1606
1606
1607 if rev:
1607 if rev:
1608 if files:
1608 if files:
1609 raise util.Abort(_('option "-r" not valid when importing '
1609 raise util.Abort(_('option "-r" not valid when importing '
1610 'files'))
1610 'files'))
1611 rev = cmdutil.revrange(repo, rev)
1611 rev = cmdutil.revrange(repo, rev)
1612 rev.sort(reverse=True)
1612 rev.sort(reverse=True)
1613 if (len(files) > 1 or len(rev) > 1) and patchname:
1613 if (len(files) > 1 or len(rev) > 1) and patchname:
1614 raise util.Abort(_('option "-n" not valid when importing multiple '
1614 raise util.Abort(_('option "-n" not valid when importing multiple '
1615 'patches'))
1615 'patches'))
1616 i = 0
1616 i = 0
1617 added = []
1617 added = []
1618 if rev:
1618 if rev:
1619 # If mq patches are applied, we can only import revisions
1619 # If mq patches are applied, we can only import revisions
1620 # that form a linear path to qbase.
1620 # that form a linear path to qbase.
1621 # Otherwise, they should form a linear path to a head.
1621 # Otherwise, they should form a linear path to a head.
1622 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1622 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1623 if len(heads) > 1:
1623 if len(heads) > 1:
1624 raise util.Abort(_('revision %d is the root of more than one '
1624 raise util.Abort(_('revision %d is the root of more than one '
1625 'branch') % rev[-1])
1625 'branch') % rev[-1])
1626 if self.applied:
1626 if self.applied:
1627 base = hex(repo.changelog.node(rev[0]))
1627 base = hex(repo.changelog.node(rev[0]))
1628 if base in [n.rev for n in self.applied]:
1628 if base in [n.rev for n in self.applied]:
1629 raise util.Abort(_('revision %d is already managed')
1629 raise util.Abort(_('revision %d is already managed')
1630 % rev[0])
1630 % rev[0])
1631 if heads != [bin(self.applied[-1].rev)]:
1631 if heads != [bin(self.applied[-1].rev)]:
1632 raise util.Abort(_('revision %d is not the parent of '
1632 raise util.Abort(_('revision %d is not the parent of '
1633 'the queue') % rev[0])
1633 'the queue') % rev[0])
1634 base = repo.changelog.rev(bin(self.applied[0].rev))
1634 base = repo.changelog.rev(bin(self.applied[0].rev))
1635 lastparent = repo.changelog.parentrevs(base)[0]
1635 lastparent = repo.changelog.parentrevs(base)[0]
1636 else:
1636 else:
1637 if heads != [repo.changelog.node(rev[0])]:
1637 if heads != [repo.changelog.node(rev[0])]:
1638 raise util.Abort(_('revision %d has unmanaged children')
1638 raise util.Abort(_('revision %d has unmanaged children')
1639 % rev[0])
1639 % rev[0])
1640 lastparent = None
1640 lastparent = None
1641
1641
1642 diffopts = self.diffopts({'git': git})
1642 diffopts = self.diffopts({'git': git})
1643 for r in rev:
1643 for r in rev:
1644 p1, p2 = repo.changelog.parentrevs(r)
1644 p1, p2 = repo.changelog.parentrevs(r)
1645 n = repo.changelog.node(r)
1645 n = repo.changelog.node(r)
1646 if p2 != nullrev:
1646 if p2 != nullrev:
1647 raise util.Abort(_('cannot import merge revision %d') % r)
1647 raise util.Abort(_('cannot import merge revision %d') % r)
1648 if lastparent and lastparent != r:
1648 if lastparent and lastparent != r:
1649 raise util.Abort(_('revision %d is not the parent of %d')
1649 raise util.Abort(_('revision %d is not the parent of %d')
1650 % (r, lastparent))
1650 % (r, lastparent))
1651 lastparent = p1
1651 lastparent = p1
1652
1652
1653 if not patchname:
1653 if not patchname:
1654 patchname = normname('%d.diff' % r)
1654 patchname = normname('%d.diff' % r)
1655 self.check_reserved_name(patchname)
1655 self.check_reserved_name(patchname)
1656 checkseries(patchname)
1656 checkseries(patchname)
1657 checkfile(patchname)
1657 checkfile(patchname)
1658 self.full_series.insert(0, patchname)
1658 self.full_series.insert(0, patchname)
1659
1659
1660 patchf = self.opener(patchname, "w")
1660 patchf = self.opener(patchname, "w")
1661 patch.export(repo, [n], fp=patchf, opts=diffopts)
1661 patch.export(repo, [n], fp=patchf, opts=diffopts)
1662 patchf.close()
1662 patchf.close()
1663
1663
1664 se = statusentry(hex(n), patchname)
1664 se = statusentry(hex(n), patchname)
1665 self.applied.insert(0, se)
1665 self.applied.insert(0, se)
1666
1666
1667 added.append(patchname)
1667 added.append(patchname)
1668 patchname = None
1668 patchname = None
1669 self.parse_series()
1669 self.parse_series()
1670 self.applied_dirty = 1
1670 self.applied_dirty = 1
1671
1671
1672 for filename in files:
1672 for filename in files:
1673 if existing:
1673 if existing:
1674 if filename == '-':
1674 if filename == '-':
1675 raise util.Abort(_('-e is incompatible with import from -'))
1675 raise util.Abort(_('-e is incompatible with import from -'))
1676 if not patchname:
1676 if not patchname:
1677 patchname = normname(filename)
1677 patchname = normname(filename)
1678 self.check_reserved_name(patchname)
1678 self.check_reserved_name(patchname)
1679 if not os.path.isfile(self.join(patchname)):
1679 if not os.path.isfile(self.join(patchname)):
1680 raise util.Abort(_("patch %s does not exist") % patchname)
1680 raise util.Abort(_("patch %s does not exist") % patchname)
1681 else:
1681 else:
1682 try:
1682 try:
1683 if filename == '-':
1683 if filename == '-':
1684 if not patchname:
1684 if not patchname:
1685 raise util.Abort(
1685 raise util.Abort(
1686 _('need --name to import a patch from -'))
1686 _('need --name to import a patch from -'))
1687 text = sys.stdin.read()
1687 text = sys.stdin.read()
1688 else:
1688 else:
1689 text = url.open(self.ui, filename).read()
1689 text = url.open(self.ui, filename).read()
1690 except (OSError, IOError):
1690 except (OSError, IOError):
1691 raise util.Abort(_("unable to read %s") % filename)
1691 raise util.Abort(_("unable to read %s") % filename)
1692 if not patchname:
1692 if not patchname:
1693 patchname = normname(os.path.basename(filename))
1693 patchname = normname(os.path.basename(filename))
1694 self.check_reserved_name(patchname)
1694 self.check_reserved_name(patchname)
1695 checkfile(patchname)
1695 checkfile(patchname)
1696 patchf = self.opener(patchname, "w")
1696 patchf = self.opener(patchname, "w")
1697 patchf.write(text)
1697 patchf.write(text)
1698 if not force:
1698 if not force:
1699 checkseries(patchname)
1699 checkseries(patchname)
1700 if patchname not in self.series:
1700 if patchname not in self.series:
1701 index = self.full_series_end() + i
1701 index = self.full_series_end() + i
1702 self.full_series[index:index] = [patchname]
1702 self.full_series[index:index] = [patchname]
1703 self.parse_series()
1703 self.parse_series()
1704 self.ui.warn(_("adding %s to series file\n") % patchname)
1704 self.ui.warn(_("adding %s to series file\n") % patchname)
1705 i += 1
1705 i += 1
1706 added.append(patchname)
1706 added.append(patchname)
1707 patchname = None
1707 patchname = None
1708 self.series_dirty = 1
1708 self.series_dirty = 1
1709 qrepo = self.qrepo()
1709 qrepo = self.qrepo()
1710 if qrepo:
1710 if qrepo:
1711 qrepo.add(added)
1711 qrepo.add(added)
1712
1712
1713 def delete(ui, repo, *patches, **opts):
1713 def delete(ui, repo, *patches, **opts):
1714 """remove patches from queue
1714 """remove patches from queue
1715
1715
1716 The patches must not be applied, and at least one patch is required. With
1716 The patches must not be applied, and at least one patch is required. With
1717 -k/--keep, the patch files are preserved in the patch directory.
1717 -k/--keep, the patch files are preserved in the patch directory.
1718
1718
1719 To stop managing a patch and move it into permanent history,
1719 To stop managing a patch and move it into permanent history,
1720 use the qfinish command."""
1720 use the qfinish command."""
1721 q = repo.mq
1721 q = repo.mq
1722 q.delete(repo, patches, opts)
1722 q.delete(repo, patches, opts)
1723 q.save_dirty()
1723 q.save_dirty()
1724 return 0
1724 return 0
1725
1725
1726 def applied(ui, repo, patch=None, **opts):
1726 def applied(ui, repo, patch=None, **opts):
1727 """print the patches already applied"""
1727 """print the patches already applied"""
1728
1728
1729 q = repo.mq
1729 q = repo.mq
1730 l = len(q.applied)
1730 l = len(q.applied)
1731
1731
1732 if patch:
1732 if patch:
1733 if patch not in q.series:
1733 if patch not in q.series:
1734 raise util.Abort(_("patch %s is not in series file") % patch)
1734 raise util.Abort(_("patch %s is not in series file") % patch)
1735 end = q.series.index(patch) + 1
1735 end = q.series.index(patch) + 1
1736 else:
1736 else:
1737 end = q.series_end(True)
1737 end = q.series_end(True)
1738
1738
1739 if opts.get('last') and not end:
1739 if opts.get('last') and not end:
1740 ui.write(_("no patches applied\n"))
1740 ui.write(_("no patches applied\n"))
1741 return 1
1741 return 1
1742 elif opts.get('last') and end == 1:
1742 elif opts.get('last') and end == 1:
1743 ui.write(_("only one patch applied\n"))
1743 ui.write(_("only one patch applied\n"))
1744 return 1
1744 return 1
1745 elif opts.get('last'):
1745 elif opts.get('last'):
1746 start = end - 2
1746 start = end - 2
1747 end = 1
1747 end = 1
1748 else:
1748 else:
1749 start = 0
1749 start = 0
1750
1750
1751 return q.qseries(repo, length=end, start=start, status='A',
1751 return q.qseries(repo, length=end, start=start, status='A',
1752 summary=opts.get('summary'))
1752 summary=opts.get('summary'))
1753
1753
1754 def unapplied(ui, repo, patch=None, **opts):
1754 def unapplied(ui, repo, patch=None, **opts):
1755 """print the patches not yet applied"""
1755 """print the patches not yet applied"""
1756
1756
1757 q = repo.mq
1757 q = repo.mq
1758 if patch:
1758 if patch:
1759 if patch not in q.series:
1759 if patch not in q.series:
1760 raise util.Abort(_("patch %s is not in series file") % patch)
1760 raise util.Abort(_("patch %s is not in series file") % patch)
1761 start = q.series.index(patch) + 1
1761 start = q.series.index(patch) + 1
1762 else:
1762 else:
1763 start = q.series_end(True)
1763 start = q.series_end(True)
1764
1764
1765 if start == len(q.series) and opts.get('first'):
1765 if start == len(q.series) and opts.get('first'):
1766 ui.write(_("all patches applied\n"))
1766 ui.write(_("all patches applied\n"))
1767 return 1
1767 return 1
1768
1768
1769 length = opts.get('first') and 1 or None
1769 length = opts.get('first') and 1 or None
1770 return q.qseries(repo, start=start, length=length, status='U',
1770 return q.qseries(repo, start=start, length=length, status='U',
1771 summary=opts.get('summary'))
1771 summary=opts.get('summary'))
1772
1772
1773 def qimport(ui, repo, *filename, **opts):
1773 def qimport(ui, repo, *filename, **opts):
1774 """import a patch
1774 """import a patch
1775
1775
1776 The patch is inserted into the series after the last applied
1776 The patch is inserted into the series after the last applied
1777 patch. If no patches have been applied, qimport prepends the patch
1777 patch. If no patches have been applied, qimport prepends the patch
1778 to the series.
1778 to the series.
1779
1779
1780 The patch will have the same name as its source file unless you
1780 The patch will have the same name as its source file unless you
1781 give it a new one with -n/--name.
1781 give it a new one with -n/--name.
1782
1782
1783 You can register an existing patch inside the patch directory with
1783 You can register an existing patch inside the patch directory with
1784 the -e/--existing flag.
1784 the -e/--existing flag.
1785
1785
1786 With -f/--force, an existing patch of the same name will be
1786 With -f/--force, an existing patch of the same name will be
1787 overwritten.
1787 overwritten.
1788
1788
1789 An existing changeset may be placed under mq control with -r/--rev
1789 An existing changeset may be placed under mq control with -r/--rev
1790 (e.g. qimport --rev tip -n patch will place tip under mq control).
1790 (e.g. qimport --rev tip -n patch will place tip under mq control).
1791 With -g/--git, patches imported with --rev will use the git diff
1791 With -g/--git, patches imported with --rev will use the git diff
1792 format. See the diffs help topic for information on why this is
1792 format. See the diffs help topic for information on why this is
1793 important for preserving rename/copy information and permission
1793 important for preserving rename/copy information and permission
1794 changes.
1794 changes.
1795
1795
1796 To import a patch from standard input, pass - as the patch file.
1796 To import a patch from standard input, pass - as the patch file.
1797 When importing from standard input, a patch name must be specified
1797 When importing from standard input, a patch name must be specified
1798 using the --name flag.
1798 using the --name flag.
1799 """
1799 """
1800 q = repo.mq
1800 q = repo.mq
1801 q.qimport(repo, filename, patchname=opts['name'],
1801 q.qimport(repo, filename, patchname=opts['name'],
1802 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1802 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1803 git=opts['git'])
1803 git=opts['git'])
1804 q.save_dirty()
1804 q.save_dirty()
1805
1805
1806 if opts.get('push') and not opts.get('rev'):
1806 if opts.get('push') and not opts.get('rev'):
1807 return q.push(repo, None)
1807 return q.push(repo, None)
1808 return 0
1808 return 0
1809
1809
1810 def init(ui, repo, **opts):
1810 def init(ui, repo, **opts):
1811 """init a new queue repository (DEPRECATED)
1811 """init a new queue repository (DEPRECATED)
1812
1812
1813 The queue repository is unversioned by default. If
1813 The queue repository is unversioned by default. If
1814 -c/--create-repo is specified, qinit will create a separate nested
1814 -c/--create-repo is specified, qinit will create a separate nested
1815 repository for patches (qinit -c may also be run later to convert
1815 repository for patches (qinit -c may also be run later to convert
1816 an unversioned patch repository into a versioned one). You can use
1816 an unversioned patch repository into a versioned one). You can use
1817 qcommit to commit changes to this queue repository.
1817 qcommit to commit changes to this queue repository.
1818
1818
1819 This command is depreacted. Without -c, it's implied by other relevant
1819 This command is deprecated. Without -c, it's implied by other relevant
1820 commands. With -c, use hg -Q init instead."""
1820 commands. With -c, use hg -Q init instead."""
1821 q = repo.mq
1821 q = repo.mq
1822 r = q.init(repo, create=opts['create_repo'])
1822 r = q.init(repo, create=opts['create_repo'])
1823 q.save_dirty()
1823 q.save_dirty()
1824 if r:
1824 if r:
1825 if not os.path.exists(r.wjoin('.hgignore')):
1825 if not os.path.exists(r.wjoin('.hgignore')):
1826 fp = r.wopener('.hgignore', 'w')
1826 fp = r.wopener('.hgignore', 'w')
1827 fp.write('^\\.hg\n')
1827 fp.write('^\\.hg\n')
1828 fp.write('^\\.mq\n')
1828 fp.write('^\\.mq\n')
1829 fp.write('syntax: glob\n')
1829 fp.write('syntax: glob\n')
1830 fp.write('status\n')
1830 fp.write('status\n')
1831 fp.write('guards\n')
1831 fp.write('guards\n')
1832 fp.close()
1832 fp.close()
1833 if not os.path.exists(r.wjoin('series')):
1833 if not os.path.exists(r.wjoin('series')):
1834 r.wopener('series', 'w').close()
1834 r.wopener('series', 'w').close()
1835 r.add(['.hgignore', 'series'])
1835 r.add(['.hgignore', 'series'])
1836 commands.add(ui, r)
1836 commands.add(ui, r)
1837 return 0
1837 return 0
1838
1838
1839 def clone(ui, source, dest=None, **opts):
1839 def clone(ui, source, dest=None, **opts):
1840 '''clone main and patch repository at same time
1840 '''clone main and patch repository at same time
1841
1841
1842 If source is local, destination will have no patches applied. If
1842 If source is local, destination will have no patches applied. If
1843 source is remote, this command can not check if patches are
1843 source is remote, this command can not check if patches are
1844 applied in source, so cannot guarantee that patches are not
1844 applied in source, so cannot guarantee that patches are not
1845 applied in destination. If you clone remote repository, be sure
1845 applied in destination. If you clone remote repository, be sure
1846 before that it has no patches applied.
1846 before that it has no patches applied.
1847
1847
1848 Source patch repository is looked for in <src>/.hg/patches by
1848 Source patch repository is looked for in <src>/.hg/patches by
1849 default. Use -p <url> to change.
1849 default. Use -p <url> to change.
1850
1850
1851 The patch directory must be a nested Mercurial repository, as
1851 The patch directory must be a nested Mercurial repository, as
1852 would be created by qinit -c.
1852 would be created by qinit -c.
1853 '''
1853 '''
1854 def patchdir(repo):
1854 def patchdir(repo):
1855 url = repo.url()
1855 url = repo.url()
1856 if url.endswith('/'):
1856 if url.endswith('/'):
1857 url = url[:-1]
1857 url = url[:-1]
1858 return url + '/.hg/patches'
1858 return url + '/.hg/patches'
1859 if dest is None:
1859 if dest is None:
1860 dest = hg.defaultdest(source)
1860 dest = hg.defaultdest(source)
1861 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1861 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1862 if opts['patches']:
1862 if opts['patches']:
1863 patchespath = ui.expandpath(opts['patches'])
1863 patchespath = ui.expandpath(opts['patches'])
1864 else:
1864 else:
1865 patchespath = patchdir(sr)
1865 patchespath = patchdir(sr)
1866 try:
1866 try:
1867 hg.repository(ui, patchespath)
1867 hg.repository(ui, patchespath)
1868 except error.RepoError:
1868 except error.RepoError:
1869 raise util.Abort(_('versioned patch repository not found'
1869 raise util.Abort(_('versioned patch repository not found'
1870 ' (see qinit -c)'))
1870 ' (see qinit -c)'))
1871 qbase, destrev = None, None
1871 qbase, destrev = None, None
1872 if sr.local():
1872 if sr.local():
1873 if sr.mq.applied:
1873 if sr.mq.applied:
1874 qbase = bin(sr.mq.applied[0].rev)
1874 qbase = bin(sr.mq.applied[0].rev)
1875 if not hg.islocal(dest):
1875 if not hg.islocal(dest):
1876 heads = set(sr.heads())
1876 heads = set(sr.heads())
1877 destrev = list(heads.difference(sr.heads(qbase)))
1877 destrev = list(heads.difference(sr.heads(qbase)))
1878 destrev.append(sr.changelog.parents(qbase)[0])
1878 destrev.append(sr.changelog.parents(qbase)[0])
1879 elif sr.capable('lookup'):
1879 elif sr.capable('lookup'):
1880 try:
1880 try:
1881 qbase = sr.lookup('qbase')
1881 qbase = sr.lookup('qbase')
1882 except error.RepoError:
1882 except error.RepoError:
1883 pass
1883 pass
1884 ui.note(_('cloning main repository\n'))
1884 ui.note(_('cloning main repository\n'))
1885 sr, dr = hg.clone(ui, sr.url(), dest,
1885 sr, dr = hg.clone(ui, sr.url(), dest,
1886 pull=opts['pull'],
1886 pull=opts['pull'],
1887 rev=destrev,
1887 rev=destrev,
1888 update=False,
1888 update=False,
1889 stream=opts['uncompressed'])
1889 stream=opts['uncompressed'])
1890 ui.note(_('cloning patch repository\n'))
1890 ui.note(_('cloning patch repository\n'))
1891 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1891 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1892 pull=opts['pull'], update=not opts['noupdate'],
1892 pull=opts['pull'], update=not opts['noupdate'],
1893 stream=opts['uncompressed'])
1893 stream=opts['uncompressed'])
1894 if dr.local():
1894 if dr.local():
1895 if qbase:
1895 if qbase:
1896 ui.note(_('stripping applied patches from destination '
1896 ui.note(_('stripping applied patches from destination '
1897 'repository\n'))
1897 'repository\n'))
1898 dr.mq.strip(dr, qbase, update=False, backup=None)
1898 dr.mq.strip(dr, qbase, update=False, backup=None)
1899 if not opts['noupdate']:
1899 if not opts['noupdate']:
1900 ui.note(_('updating destination repository\n'))
1900 ui.note(_('updating destination repository\n'))
1901 hg.update(dr, dr.changelog.tip())
1901 hg.update(dr, dr.changelog.tip())
1902
1902
1903 def commit(ui, repo, *pats, **opts):
1903 def commit(ui, repo, *pats, **opts):
1904 """commit changes in the queue repository (DEPRECATED)
1904 """commit changes in the queue repository (DEPRECATED)
1905
1905
1906 This command is deprecated; use hg -Q commit instead."""
1906 This command is deprecated; use hg -Q commit instead."""
1907 q = repo.mq
1907 q = repo.mq
1908 r = q.qrepo()
1908 r = q.qrepo()
1909 if not r:
1909 if not r:
1910 raise util.Abort('no queue repository')
1910 raise util.Abort('no queue repository')
1911 commands.commit(r.ui, r, *pats, **opts)
1911 commands.commit(r.ui, r, *pats, **opts)
1912
1912
1913 def series(ui, repo, **opts):
1913 def series(ui, repo, **opts):
1914 """print the entire series file"""
1914 """print the entire series file"""
1915 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1915 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1916 return 0
1916 return 0
1917
1917
1918 def top(ui, repo, **opts):
1918 def top(ui, repo, **opts):
1919 """print the name of the current patch"""
1919 """print the name of the current patch"""
1920 q = repo.mq
1920 q = repo.mq
1921 t = q.applied and q.series_end(True) or 0
1921 t = q.applied and q.series_end(True) or 0
1922 if t:
1922 if t:
1923 return q.qseries(repo, start=t - 1, length=1, status='A',
1923 return q.qseries(repo, start=t - 1, length=1, status='A',
1924 summary=opts.get('summary'))
1924 summary=opts.get('summary'))
1925 else:
1925 else:
1926 ui.write(_("no patches applied\n"))
1926 ui.write(_("no patches applied\n"))
1927 return 1
1927 return 1
1928
1928
1929 def next(ui, repo, **opts):
1929 def next(ui, repo, **opts):
1930 """print the name of the next patch"""
1930 """print the name of the next patch"""
1931 q = repo.mq
1931 q = repo.mq
1932 end = q.series_end()
1932 end = q.series_end()
1933 if end == len(q.series):
1933 if end == len(q.series):
1934 ui.write(_("all patches applied\n"))
1934 ui.write(_("all patches applied\n"))
1935 return 1
1935 return 1
1936 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1936 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1937
1937
1938 def prev(ui, repo, **opts):
1938 def prev(ui, repo, **opts):
1939 """print the name of the previous patch"""
1939 """print the name of the previous patch"""
1940 q = repo.mq
1940 q = repo.mq
1941 l = len(q.applied)
1941 l = len(q.applied)
1942 if l == 1:
1942 if l == 1:
1943 ui.write(_("only one patch applied\n"))
1943 ui.write(_("only one patch applied\n"))
1944 return 1
1944 return 1
1945 if not l:
1945 if not l:
1946 ui.write(_("no patches applied\n"))
1946 ui.write(_("no patches applied\n"))
1947 return 1
1947 return 1
1948 return q.qseries(repo, start=l - 2, length=1, status='A',
1948 return q.qseries(repo, start=l - 2, length=1, status='A',
1949 summary=opts.get('summary'))
1949 summary=opts.get('summary'))
1950
1950
1951 def setupheaderopts(ui, opts):
1951 def setupheaderopts(ui, opts):
1952 if not opts.get('user') and opts.get('currentuser'):
1952 if not opts.get('user') and opts.get('currentuser'):
1953 opts['user'] = ui.username()
1953 opts['user'] = ui.username()
1954 if not opts.get('date') and opts.get('currentdate'):
1954 if not opts.get('date') and opts.get('currentdate'):
1955 opts['date'] = "%d %d" % util.makedate()
1955 opts['date'] = "%d %d" % util.makedate()
1956
1956
1957 def new(ui, repo, patch, *args, **opts):
1957 def new(ui, repo, patch, *args, **opts):
1958 """create a new patch
1958 """create a new patch
1959
1959
1960 qnew creates a new patch on top of the currently-applied patch (if
1960 qnew creates a new patch on top of the currently-applied patch (if
1961 any). It will refuse to run if there are any outstanding changes
1961 any). It will refuse to run if there are any outstanding changes
1962 unless -f/--force is specified, in which case the patch will be
1962 unless -f/--force is specified, in which case the patch will be
1963 initialized with them. You may also use -I/--include,
1963 initialized with them. You may also use -I/--include,
1964 -X/--exclude, and/or a list of files after the patch name to add
1964 -X/--exclude, and/or a list of files after the patch name to add
1965 only changes to matching files to the new patch, leaving the rest
1965 only changes to matching files to the new patch, leaving the rest
1966 as uncommitted modifications.
1966 as uncommitted modifications.
1967
1967
1968 -u/--user and -d/--date can be used to set the (given) user and
1968 -u/--user and -d/--date can be used to set the (given) user and
1969 date, respectively. -U/--currentuser and -D/--currentdate set user
1969 date, respectively. -U/--currentuser and -D/--currentdate set user
1970 to current user and date to current date.
1970 to current user and date to current date.
1971
1971
1972 -e/--edit, -m/--message or -l/--logfile set the patch header as
1972 -e/--edit, -m/--message or -l/--logfile set the patch header as
1973 well as the commit message. If none is specified, the header is
1973 well as the commit message. If none is specified, the header is
1974 empty and the commit message is '[mq]: PATCH'.
1974 empty and the commit message is '[mq]: PATCH'.
1975
1975
1976 Use the -g/--git option to keep the patch in the git extended diff
1976 Use the -g/--git option to keep the patch in the git extended diff
1977 format. Read the diffs help topic for more information on why this
1977 format. Read the diffs help topic for more information on why this
1978 is important for preserving permission changes and copy/rename
1978 is important for preserving permission changes and copy/rename
1979 information.
1979 information.
1980 """
1980 """
1981 msg = cmdutil.logmessage(opts)
1981 msg = cmdutil.logmessage(opts)
1982 def getmsg():
1982 def getmsg():
1983 return ui.edit(msg, ui.username())
1983 return ui.edit(msg, ui.username())
1984 q = repo.mq
1984 q = repo.mq
1985 opts['msg'] = msg
1985 opts['msg'] = msg
1986 if opts.get('edit'):
1986 if opts.get('edit'):
1987 opts['msg'] = getmsg
1987 opts['msg'] = getmsg
1988 else:
1988 else:
1989 opts['msg'] = msg
1989 opts['msg'] = msg
1990 setupheaderopts(ui, opts)
1990 setupheaderopts(ui, opts)
1991 q.new(repo, patch, *args, **opts)
1991 q.new(repo, patch, *args, **opts)
1992 q.save_dirty()
1992 q.save_dirty()
1993 return 0
1993 return 0
1994
1994
1995 def refresh(ui, repo, *pats, **opts):
1995 def refresh(ui, repo, *pats, **opts):
1996 """update the current patch
1996 """update the current patch
1997
1997
1998 If any file patterns are provided, the refreshed patch will
1998 If any file patterns are provided, the refreshed patch will
1999 contain only the modifications that match those patterns; the
1999 contain only the modifications that match those patterns; the
2000 remaining modifications will remain in the working directory.
2000 remaining modifications will remain in the working directory.
2001
2001
2002 If -s/--short is specified, files currently included in the patch
2002 If -s/--short is specified, files currently included in the patch
2003 will be refreshed just like matched files and remain in the patch.
2003 will be refreshed just like matched files and remain in the patch.
2004
2004
2005 hg add/remove/copy/rename work as usual, though you might want to
2005 hg add/remove/copy/rename work as usual, though you might want to
2006 use git-style patches (-g/--git or [diff] git=1) to track copies
2006 use git-style patches (-g/--git or [diff] git=1) to track copies
2007 and renames. See the diffs help topic for more information on the
2007 and renames. See the diffs help topic for more information on the
2008 git diff format.
2008 git diff format.
2009 """
2009 """
2010 q = repo.mq
2010 q = repo.mq
2011 message = cmdutil.logmessage(opts)
2011 message = cmdutil.logmessage(opts)
2012 if opts['edit']:
2012 if opts['edit']:
2013 if not q.applied:
2013 if not q.applied:
2014 ui.write(_("no patches applied\n"))
2014 ui.write(_("no patches applied\n"))
2015 return 1
2015 return 1
2016 if message:
2016 if message:
2017 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2017 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2018 patch = q.applied[-1].name
2018 patch = q.applied[-1].name
2019 ph = patchheader(q.join(patch))
2019 ph = patchheader(q.join(patch))
2020 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2020 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2021 setupheaderopts(ui, opts)
2021 setupheaderopts(ui, opts)
2022 ret = q.refresh(repo, pats, msg=message, **opts)
2022 ret = q.refresh(repo, pats, msg=message, **opts)
2023 q.save_dirty()
2023 q.save_dirty()
2024 return ret
2024 return ret
2025
2025
2026 def diff(ui, repo, *pats, **opts):
2026 def diff(ui, repo, *pats, **opts):
2027 """diff of the current patch and subsequent modifications
2027 """diff of the current patch and subsequent modifications
2028
2028
2029 Shows a diff which includes the current patch as well as any
2029 Shows a diff which includes the current patch as well as any
2030 changes which have been made in the working directory since the
2030 changes which have been made in the working directory since the
2031 last refresh (thus showing what the current patch would become
2031 last refresh (thus showing what the current patch would become
2032 after a qrefresh).
2032 after a qrefresh).
2033
2033
2034 Use 'hg diff' if you only want to see the changes made since the
2034 Use 'hg diff' if you only want to see the changes made since the
2035 last qrefresh, or 'hg export qtip' if you want to see changes made
2035 last qrefresh, or 'hg export qtip' if you want to see changes made
2036 by the current patch without including changes made since the
2036 by the current patch without including changes made since the
2037 qrefresh.
2037 qrefresh.
2038 """
2038 """
2039 repo.mq.diff(repo, pats, opts)
2039 repo.mq.diff(repo, pats, opts)
2040 return 0
2040 return 0
2041
2041
2042 def fold(ui, repo, *files, **opts):
2042 def fold(ui, repo, *files, **opts):
2043 """fold the named patches into the current patch
2043 """fold the named patches into the current patch
2044
2044
2045 Patches must not yet be applied. Each patch will be successively
2045 Patches must not yet be applied. Each patch will be successively
2046 applied to the current patch in the order given. If all the
2046 applied to the current patch in the order given. If all the
2047 patches apply successfully, the current patch will be refreshed
2047 patches apply successfully, the current patch will be refreshed
2048 with the new cumulative patch, and the folded patches will be
2048 with the new cumulative patch, and the folded patches will be
2049 deleted. With -k/--keep, the folded patch files will not be
2049 deleted. With -k/--keep, the folded patch files will not be
2050 removed afterwards.
2050 removed afterwards.
2051
2051
2052 The header for each folded patch will be concatenated with the
2052 The header for each folded patch will be concatenated with the
2053 current patch header, separated by a line of '* * *'."""
2053 current patch header, separated by a line of '* * *'."""
2054
2054
2055 q = repo.mq
2055 q = repo.mq
2056
2056
2057 if not files:
2057 if not files:
2058 raise util.Abort(_('qfold requires at least one patch name'))
2058 raise util.Abort(_('qfold requires at least one patch name'))
2059 if not q.check_toppatch(repo)[0]:
2059 if not q.check_toppatch(repo)[0]:
2060 raise util.Abort(_('No patches applied'))
2060 raise util.Abort(_('No patches applied'))
2061 q.check_localchanges(repo)
2061 q.check_localchanges(repo)
2062
2062
2063 message = cmdutil.logmessage(opts)
2063 message = cmdutil.logmessage(opts)
2064 if opts['edit']:
2064 if opts['edit']:
2065 if message:
2065 if message:
2066 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2066 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2067
2067
2068 parent = q.lookup('qtip')
2068 parent = q.lookup('qtip')
2069 patches = []
2069 patches = []
2070 messages = []
2070 messages = []
2071 for f in files:
2071 for f in files:
2072 p = q.lookup(f)
2072 p = q.lookup(f)
2073 if p in patches or p == parent:
2073 if p in patches or p == parent:
2074 ui.warn(_('Skipping already folded patch %s') % p)
2074 ui.warn(_('Skipping already folded patch %s') % p)
2075 if q.isapplied(p):
2075 if q.isapplied(p):
2076 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2076 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2077 patches.append(p)
2077 patches.append(p)
2078
2078
2079 for p in patches:
2079 for p in patches:
2080 if not message:
2080 if not message:
2081 ph = patchheader(q.join(p))
2081 ph = patchheader(q.join(p))
2082 if ph.message:
2082 if ph.message:
2083 messages.append(ph.message)
2083 messages.append(ph.message)
2084 pf = q.join(p)
2084 pf = q.join(p)
2085 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2085 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2086 if not patchsuccess:
2086 if not patchsuccess:
2087 raise util.Abort(_('Error folding patch %s') % p)
2087 raise util.Abort(_('Error folding patch %s') % p)
2088 patch.updatedir(ui, repo, files)
2088 patch.updatedir(ui, repo, files)
2089
2089
2090 if not message:
2090 if not message:
2091 ph = patchheader(q.join(parent))
2091 ph = patchheader(q.join(parent))
2092 message, user = ph.message, ph.user
2092 message, user = ph.message, ph.user
2093 for msg in messages:
2093 for msg in messages:
2094 message.append('* * *')
2094 message.append('* * *')
2095 message.extend(msg)
2095 message.extend(msg)
2096 message = '\n'.join(message)
2096 message = '\n'.join(message)
2097
2097
2098 if opts['edit']:
2098 if opts['edit']:
2099 message = ui.edit(message, user or ui.username())
2099 message = ui.edit(message, user or ui.username())
2100
2100
2101 diffopts = q.patchopts(q.diffopts(), *patches)
2101 diffopts = q.patchopts(q.diffopts(), *patches)
2102 q.refresh(repo, msg=message, git=diffopts.git)
2102 q.refresh(repo, msg=message, git=diffopts.git)
2103 q.delete(repo, patches, opts)
2103 q.delete(repo, patches, opts)
2104 q.save_dirty()
2104 q.save_dirty()
2105
2105
2106 def goto(ui, repo, patch, **opts):
2106 def goto(ui, repo, patch, **opts):
2107 '''push or pop patches until named patch is at top of stack'''
2107 '''push or pop patches until named patch is at top of stack'''
2108 q = repo.mq
2108 q = repo.mq
2109 patch = q.lookup(patch)
2109 patch = q.lookup(patch)
2110 if q.isapplied(patch):
2110 if q.isapplied(patch):
2111 ret = q.pop(repo, patch, force=opts['force'])
2111 ret = q.pop(repo, patch, force=opts['force'])
2112 else:
2112 else:
2113 ret = q.push(repo, patch, force=opts['force'])
2113 ret = q.push(repo, patch, force=opts['force'])
2114 q.save_dirty()
2114 q.save_dirty()
2115 return ret
2115 return ret
2116
2116
2117 def guard(ui, repo, *args, **opts):
2117 def guard(ui, repo, *args, **opts):
2118 '''set or print guards for a patch
2118 '''set or print guards for a patch
2119
2119
2120 Guards control whether a patch can be pushed. A patch with no
2120 Guards control whether a patch can be pushed. A patch with no
2121 guards is always pushed. A patch with a positive guard ("+foo") is
2121 guards is always pushed. A patch with a positive guard ("+foo") is
2122 pushed only if the qselect command has activated it. A patch with
2122 pushed only if the qselect command has activated it. A patch with
2123 a negative guard ("-foo") is never pushed if the qselect command
2123 a negative guard ("-foo") is never pushed if the qselect command
2124 has activated it.
2124 has activated it.
2125
2125
2126 With no arguments, print the currently active guards.
2126 With no arguments, print the currently active guards.
2127 With arguments, set guards for the named patch.
2127 With arguments, set guards for the named patch.
2128 NOTE: Specifying negative guards now requires '--'.
2128 NOTE: Specifying negative guards now requires '--'.
2129
2129
2130 To set guards on another patch::
2130 To set guards on another patch::
2131
2131
2132 hg qguard -- other.patch +2.6.17 -stable
2132 hg qguard -- other.patch +2.6.17 -stable
2133 '''
2133 '''
2134 def status(idx):
2134 def status(idx):
2135 guards = q.series_guards[idx] or ['unguarded']
2135 guards = q.series_guards[idx] or ['unguarded']
2136 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2136 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2137 q = repo.mq
2137 q = repo.mq
2138 patch = None
2138 patch = None
2139 args = list(args)
2139 args = list(args)
2140 if opts['list']:
2140 if opts['list']:
2141 if args or opts['none']:
2141 if args or opts['none']:
2142 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2142 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2143 for i in xrange(len(q.series)):
2143 for i in xrange(len(q.series)):
2144 status(i)
2144 status(i)
2145 return
2145 return
2146 if not args or args[0][0:1] in '-+':
2146 if not args or args[0][0:1] in '-+':
2147 if not q.applied:
2147 if not q.applied:
2148 raise util.Abort(_('no patches applied'))
2148 raise util.Abort(_('no patches applied'))
2149 patch = q.applied[-1].name
2149 patch = q.applied[-1].name
2150 if patch is None and args[0][0:1] not in '-+':
2150 if patch is None and args[0][0:1] not in '-+':
2151 patch = args.pop(0)
2151 patch = args.pop(0)
2152 if patch is None:
2152 if patch is None:
2153 raise util.Abort(_('no patch to work with'))
2153 raise util.Abort(_('no patch to work with'))
2154 if args or opts['none']:
2154 if args or opts['none']:
2155 idx = q.find_series(patch)
2155 idx = q.find_series(patch)
2156 if idx is None:
2156 if idx is None:
2157 raise util.Abort(_('no patch named %s') % patch)
2157 raise util.Abort(_('no patch named %s') % patch)
2158 q.set_guards(idx, args)
2158 q.set_guards(idx, args)
2159 q.save_dirty()
2159 q.save_dirty()
2160 else:
2160 else:
2161 status(q.series.index(q.lookup(patch)))
2161 status(q.series.index(q.lookup(patch)))
2162
2162
2163 def header(ui, repo, patch=None):
2163 def header(ui, repo, patch=None):
2164 """print the header of the topmost or specified patch"""
2164 """print the header of the topmost or specified patch"""
2165 q = repo.mq
2165 q = repo.mq
2166
2166
2167 if patch:
2167 if patch:
2168 patch = q.lookup(patch)
2168 patch = q.lookup(patch)
2169 else:
2169 else:
2170 if not q.applied:
2170 if not q.applied:
2171 ui.write('no patches applied\n')
2171 ui.write('no patches applied\n')
2172 return 1
2172 return 1
2173 patch = q.lookup('qtip')
2173 patch = q.lookup('qtip')
2174 ph = patchheader(repo.mq.join(patch))
2174 ph = patchheader(repo.mq.join(patch))
2175
2175
2176 ui.write('\n'.join(ph.message) + '\n')
2176 ui.write('\n'.join(ph.message) + '\n')
2177
2177
2178 def lastsavename(path):
2178 def lastsavename(path):
2179 (directory, base) = os.path.split(path)
2179 (directory, base) = os.path.split(path)
2180 names = os.listdir(directory)
2180 names = os.listdir(directory)
2181 namere = re.compile("%s.([0-9]+)" % base)
2181 namere = re.compile("%s.([0-9]+)" % base)
2182 maxindex = None
2182 maxindex = None
2183 maxname = None
2183 maxname = None
2184 for f in names:
2184 for f in names:
2185 m = namere.match(f)
2185 m = namere.match(f)
2186 if m:
2186 if m:
2187 index = int(m.group(1))
2187 index = int(m.group(1))
2188 if maxindex is None or index > maxindex:
2188 if maxindex is None or index > maxindex:
2189 maxindex = index
2189 maxindex = index
2190 maxname = f
2190 maxname = f
2191 if maxname:
2191 if maxname:
2192 return (os.path.join(directory, maxname), maxindex)
2192 return (os.path.join(directory, maxname), maxindex)
2193 return (None, None)
2193 return (None, None)
2194
2194
2195 def savename(path):
2195 def savename(path):
2196 (last, index) = lastsavename(path)
2196 (last, index) = lastsavename(path)
2197 if last is None:
2197 if last is None:
2198 index = 0
2198 index = 0
2199 newpath = path + ".%d" % (index + 1)
2199 newpath = path + ".%d" % (index + 1)
2200 return newpath
2200 return newpath
2201
2201
2202 def push(ui, repo, patch=None, **opts):
2202 def push(ui, repo, patch=None, **opts):
2203 """push the next patch onto the stack
2203 """push the next patch onto the stack
2204
2204
2205 When -f/--force is applied, all local changes in patched files
2205 When -f/--force is applied, all local changes in patched files
2206 will be lost.
2206 will be lost.
2207 """
2207 """
2208 q = repo.mq
2208 q = repo.mq
2209 mergeq = None
2209 mergeq = None
2210
2210
2211 if opts['merge']:
2211 if opts['merge']:
2212 if opts['name']:
2212 if opts['name']:
2213 newpath = repo.join(opts['name'])
2213 newpath = repo.join(opts['name'])
2214 else:
2214 else:
2215 newpath, i = lastsavename(q.path)
2215 newpath, i = lastsavename(q.path)
2216 if not newpath:
2216 if not newpath:
2217 ui.warn(_("no saved queues found, please use -n\n"))
2217 ui.warn(_("no saved queues found, please use -n\n"))
2218 return 1
2218 return 1
2219 mergeq = queue(ui, repo.join(""), newpath)
2219 mergeq = queue(ui, repo.join(""), newpath)
2220 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2220 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2221 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2221 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2222 mergeq=mergeq, all=opts.get('all'))
2222 mergeq=mergeq, all=opts.get('all'))
2223 return ret
2223 return ret
2224
2224
2225 def pop(ui, repo, patch=None, **opts):
2225 def pop(ui, repo, patch=None, **opts):
2226 """pop the current patch off the stack
2226 """pop the current patch off the stack
2227
2227
2228 By default, pops off the top of the patch stack. If given a patch
2228 By default, pops off the top of the patch stack. If given a patch
2229 name, keeps popping off patches until the named patch is at the
2229 name, keeps popping off patches until the named patch is at the
2230 top of the stack.
2230 top of the stack.
2231 """
2231 """
2232 localupdate = True
2232 localupdate = True
2233 if opts['name']:
2233 if opts['name']:
2234 q = queue(ui, repo.join(""), repo.join(opts['name']))
2234 q = queue(ui, repo.join(""), repo.join(opts['name']))
2235 ui.warn(_('using patch queue: %s\n') % q.path)
2235 ui.warn(_('using patch queue: %s\n') % q.path)
2236 localupdate = False
2236 localupdate = False
2237 else:
2237 else:
2238 q = repo.mq
2238 q = repo.mq
2239 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2239 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2240 all=opts['all'])
2240 all=opts['all'])
2241 q.save_dirty()
2241 q.save_dirty()
2242 return ret
2242 return ret
2243
2243
2244 def rename(ui, repo, patch, name=None, **opts):
2244 def rename(ui, repo, patch, name=None, **opts):
2245 """rename a patch
2245 """rename a patch
2246
2246
2247 With one argument, renames the current patch to PATCH1.
2247 With one argument, renames the current patch to PATCH1.
2248 With two arguments, renames PATCH1 to PATCH2."""
2248 With two arguments, renames PATCH1 to PATCH2."""
2249
2249
2250 q = repo.mq
2250 q = repo.mq
2251
2251
2252 if not name:
2252 if not name:
2253 name = patch
2253 name = patch
2254 patch = None
2254 patch = None
2255
2255
2256 if patch:
2256 if patch:
2257 patch = q.lookup(patch)
2257 patch = q.lookup(patch)
2258 else:
2258 else:
2259 if not q.applied:
2259 if not q.applied:
2260 ui.write(_('no patches applied\n'))
2260 ui.write(_('no patches applied\n'))
2261 return
2261 return
2262 patch = q.lookup('qtip')
2262 patch = q.lookup('qtip')
2263 absdest = q.join(name)
2263 absdest = q.join(name)
2264 if os.path.isdir(absdest):
2264 if os.path.isdir(absdest):
2265 name = normname(os.path.join(name, os.path.basename(patch)))
2265 name = normname(os.path.join(name, os.path.basename(patch)))
2266 absdest = q.join(name)
2266 absdest = q.join(name)
2267 if os.path.exists(absdest):
2267 if os.path.exists(absdest):
2268 raise util.Abort(_('%s already exists') % absdest)
2268 raise util.Abort(_('%s already exists') % absdest)
2269
2269
2270 if name in q.series:
2270 if name in q.series:
2271 raise util.Abort(
2271 raise util.Abort(
2272 _('A patch named %s already exists in the series file') % name)
2272 _('A patch named %s already exists in the series file') % name)
2273
2273
2274 if ui.verbose:
2274 if ui.verbose:
2275 ui.write('renaming %s to %s\n' % (patch, name))
2275 ui.write('renaming %s to %s\n' % (patch, name))
2276 i = q.find_series(patch)
2276 i = q.find_series(patch)
2277 guards = q.guard_re.findall(q.full_series[i])
2277 guards = q.guard_re.findall(q.full_series[i])
2278 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2278 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2279 q.parse_series()
2279 q.parse_series()
2280 q.series_dirty = 1
2280 q.series_dirty = 1
2281
2281
2282 info = q.isapplied(patch)
2282 info = q.isapplied(patch)
2283 if info:
2283 if info:
2284 q.applied[info[0]] = statusentry(info[1], name)
2284 q.applied[info[0]] = statusentry(info[1], name)
2285 q.applied_dirty = 1
2285 q.applied_dirty = 1
2286
2286
2287 util.rename(q.join(patch), absdest)
2287 util.rename(q.join(patch), absdest)
2288 r = q.qrepo()
2288 r = q.qrepo()
2289 if r:
2289 if r:
2290 wlock = r.wlock()
2290 wlock = r.wlock()
2291 try:
2291 try:
2292 if r.dirstate[patch] == 'a':
2292 if r.dirstate[patch] == 'a':
2293 r.dirstate.forget(patch)
2293 r.dirstate.forget(patch)
2294 r.dirstate.add(name)
2294 r.dirstate.add(name)
2295 else:
2295 else:
2296 if r.dirstate[name] == 'r':
2296 if r.dirstate[name] == 'r':
2297 r.undelete([name])
2297 r.undelete([name])
2298 r.copy(patch, name)
2298 r.copy(patch, name)
2299 r.remove([patch], False)
2299 r.remove([patch], False)
2300 finally:
2300 finally:
2301 wlock.release()
2301 wlock.release()
2302
2302
2303 q.save_dirty()
2303 q.save_dirty()
2304
2304
2305 def restore(ui, repo, rev, **opts):
2305 def restore(ui, repo, rev, **opts):
2306 """restore the queue state saved by a revision (DEPRECATED)
2306 """restore the queue state saved by a revision (DEPRECATED)
2307
2307
2308 This command is deprecated, use rebase --mq instead."""
2308 This command is deprecated, use rebase --mq instead."""
2309 rev = repo.lookup(rev)
2309 rev = repo.lookup(rev)
2310 q = repo.mq
2310 q = repo.mq
2311 q.restore(repo, rev, delete=opts['delete'],
2311 q.restore(repo, rev, delete=opts['delete'],
2312 qupdate=opts['update'])
2312 qupdate=opts['update'])
2313 q.save_dirty()
2313 q.save_dirty()
2314 return 0
2314 return 0
2315
2315
2316 def save(ui, repo, **opts):
2316 def save(ui, repo, **opts):
2317 """save current queue state (DEPRECATED)
2317 """save current queue state (DEPRECATED)
2318
2318
2319 This command is deprecated, use rebase --mq instead."""
2319 This command is deprecated, use rebase --mq instead."""
2320 q = repo.mq
2320 q = repo.mq
2321 message = cmdutil.logmessage(opts)
2321 message = cmdutil.logmessage(opts)
2322 ret = q.save(repo, msg=message)
2322 ret = q.save(repo, msg=message)
2323 if ret:
2323 if ret:
2324 return ret
2324 return ret
2325 q.save_dirty()
2325 q.save_dirty()
2326 if opts['copy']:
2326 if opts['copy']:
2327 path = q.path
2327 path = q.path
2328 if opts['name']:
2328 if opts['name']:
2329 newpath = os.path.join(q.basepath, opts['name'])
2329 newpath = os.path.join(q.basepath, opts['name'])
2330 if os.path.exists(newpath):
2330 if os.path.exists(newpath):
2331 if not os.path.isdir(newpath):
2331 if not os.path.isdir(newpath):
2332 raise util.Abort(_('destination %s exists and is not '
2332 raise util.Abort(_('destination %s exists and is not '
2333 'a directory') % newpath)
2333 'a directory') % newpath)
2334 if not opts['force']:
2334 if not opts['force']:
2335 raise util.Abort(_('destination %s exists, '
2335 raise util.Abort(_('destination %s exists, '
2336 'use -f to force') % newpath)
2336 'use -f to force') % newpath)
2337 else:
2337 else:
2338 newpath = savename(path)
2338 newpath = savename(path)
2339 ui.warn(_("copy %s to %s\n") % (path, newpath))
2339 ui.warn(_("copy %s to %s\n") % (path, newpath))
2340 util.copyfiles(path, newpath)
2340 util.copyfiles(path, newpath)
2341 if opts['empty']:
2341 if opts['empty']:
2342 try:
2342 try:
2343 os.unlink(q.join(q.status_path))
2343 os.unlink(q.join(q.status_path))
2344 except:
2344 except:
2345 pass
2345 pass
2346 return 0
2346 return 0
2347
2347
2348 def strip(ui, repo, rev, **opts):
2348 def strip(ui, repo, rev, **opts):
2349 """strip a revision and all its descendants from the repository
2349 """strip a revision and all its descendants from the repository
2350
2350
2351 If one of the working directory's parent revisions is stripped, the
2351 If one of the working directory's parent revisions is stripped, the
2352 working directory will be updated to the parent of the stripped
2352 working directory will be updated to the parent of the stripped
2353 revision.
2353 revision.
2354 """
2354 """
2355 backup = 'all'
2355 backup = 'all'
2356 if opts['backup']:
2356 if opts['backup']:
2357 backup = 'strip'
2357 backup = 'strip'
2358 elif opts['nobackup']:
2358 elif opts['nobackup']:
2359 backup = 'none'
2359 backup = 'none'
2360
2360
2361 rev = repo.lookup(rev)
2361 rev = repo.lookup(rev)
2362 p = repo.dirstate.parents()
2362 p = repo.dirstate.parents()
2363 cl = repo.changelog
2363 cl = repo.changelog
2364 update = True
2364 update = True
2365 if p[0] == nullid:
2365 if p[0] == nullid:
2366 update = False
2366 update = False
2367 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2367 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2368 update = False
2368 update = False
2369 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2369 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2370 update = False
2370 update = False
2371
2371
2372 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2372 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2373 return 0
2373 return 0
2374
2374
2375 def select(ui, repo, *args, **opts):
2375 def select(ui, repo, *args, **opts):
2376 '''set or print guarded patches to push
2376 '''set or print guarded patches to push
2377
2377
2378 Use the qguard command to set or print guards on patch, then use
2378 Use the qguard command to set or print guards on patch, then use
2379 qselect to tell mq which guards to use. A patch will be pushed if
2379 qselect to tell mq which guards to use. A patch will be pushed if
2380 it has no guards or any positive guards match the currently
2380 it has no guards or any positive guards match the currently
2381 selected guard, but will not be pushed if any negative guards
2381 selected guard, but will not be pushed if any negative guards
2382 match the current guard. For example::
2382 match the current guard. For example::
2383
2383
2384 qguard foo.patch -stable (negative guard)
2384 qguard foo.patch -stable (negative guard)
2385 qguard bar.patch +stable (positive guard)
2385 qguard bar.patch +stable (positive guard)
2386 qselect stable
2386 qselect stable
2387
2387
2388 This activates the "stable" guard. mq will skip foo.patch (because
2388 This activates the "stable" guard. mq will skip foo.patch (because
2389 it has a negative match) but push bar.patch (because it has a
2389 it has a negative match) but push bar.patch (because it has a
2390 positive match).
2390 positive match).
2391
2391
2392 With no arguments, prints the currently active guards.
2392 With no arguments, prints the currently active guards.
2393 With one argument, sets the active guard.
2393 With one argument, sets the active guard.
2394
2394
2395 Use -n/--none to deactivate guards (no other arguments needed).
2395 Use -n/--none to deactivate guards (no other arguments needed).
2396 When no guards are active, patches with positive guards are
2396 When no guards are active, patches with positive guards are
2397 skipped and patches with negative guards are pushed.
2397 skipped and patches with negative guards are pushed.
2398
2398
2399 qselect can change the guards on applied patches. It does not pop
2399 qselect can change the guards on applied patches. It does not pop
2400 guarded patches by default. Use --pop to pop back to the last
2400 guarded patches by default. Use --pop to pop back to the last
2401 applied patch that is not guarded. Use --reapply (which implies
2401 applied patch that is not guarded. Use --reapply (which implies
2402 --pop) to push back to the current patch afterwards, but skip
2402 --pop) to push back to the current patch afterwards, but skip
2403 guarded patches.
2403 guarded patches.
2404
2404
2405 Use -s/--series to print a list of all guards in the series file
2405 Use -s/--series to print a list of all guards in the series file
2406 (no other arguments needed). Use -v for more information.'''
2406 (no other arguments needed). Use -v for more information.'''
2407
2407
2408 q = repo.mq
2408 q = repo.mq
2409 guards = q.active()
2409 guards = q.active()
2410 if args or opts['none']:
2410 if args or opts['none']:
2411 old_unapplied = q.unapplied(repo)
2411 old_unapplied = q.unapplied(repo)
2412 old_guarded = [i for i in xrange(len(q.applied)) if
2412 old_guarded = [i for i in xrange(len(q.applied)) if
2413 not q.pushable(i)[0]]
2413 not q.pushable(i)[0]]
2414 q.set_active(args)
2414 q.set_active(args)
2415 q.save_dirty()
2415 q.save_dirty()
2416 if not args:
2416 if not args:
2417 ui.status(_('guards deactivated\n'))
2417 ui.status(_('guards deactivated\n'))
2418 if not opts['pop'] and not opts['reapply']:
2418 if not opts['pop'] and not opts['reapply']:
2419 unapplied = q.unapplied(repo)
2419 unapplied = q.unapplied(repo)
2420 guarded = [i for i in xrange(len(q.applied))
2420 guarded = [i for i in xrange(len(q.applied))
2421 if not q.pushable(i)[0]]
2421 if not q.pushable(i)[0]]
2422 if len(unapplied) != len(old_unapplied):
2422 if len(unapplied) != len(old_unapplied):
2423 ui.status(_('number of unguarded, unapplied patches has '
2423 ui.status(_('number of unguarded, unapplied patches has '
2424 'changed from %d to %d\n') %
2424 'changed from %d to %d\n') %
2425 (len(old_unapplied), len(unapplied)))
2425 (len(old_unapplied), len(unapplied)))
2426 if len(guarded) != len(old_guarded):
2426 if len(guarded) != len(old_guarded):
2427 ui.status(_('number of guarded, applied patches has changed '
2427 ui.status(_('number of guarded, applied patches has changed '
2428 'from %d to %d\n') %
2428 'from %d to %d\n') %
2429 (len(old_guarded), len(guarded)))
2429 (len(old_guarded), len(guarded)))
2430 elif opts['series']:
2430 elif opts['series']:
2431 guards = {}
2431 guards = {}
2432 noguards = 0
2432 noguards = 0
2433 for gs in q.series_guards:
2433 for gs in q.series_guards:
2434 if not gs:
2434 if not gs:
2435 noguards += 1
2435 noguards += 1
2436 for g in gs:
2436 for g in gs:
2437 guards.setdefault(g, 0)
2437 guards.setdefault(g, 0)
2438 guards[g] += 1
2438 guards[g] += 1
2439 if ui.verbose:
2439 if ui.verbose:
2440 guards['NONE'] = noguards
2440 guards['NONE'] = noguards
2441 guards = guards.items()
2441 guards = guards.items()
2442 guards.sort(key=lambda x: x[0][1:])
2442 guards.sort(key=lambda x: x[0][1:])
2443 if guards:
2443 if guards:
2444 ui.note(_('guards in series file:\n'))
2444 ui.note(_('guards in series file:\n'))
2445 for guard, count in guards:
2445 for guard, count in guards:
2446 ui.note('%2d ' % count)
2446 ui.note('%2d ' % count)
2447 ui.write(guard, '\n')
2447 ui.write(guard, '\n')
2448 else:
2448 else:
2449 ui.note(_('no guards in series file\n'))
2449 ui.note(_('no guards in series file\n'))
2450 else:
2450 else:
2451 if guards:
2451 if guards:
2452 ui.note(_('active guards:\n'))
2452 ui.note(_('active guards:\n'))
2453 for g in guards:
2453 for g in guards:
2454 ui.write(g, '\n')
2454 ui.write(g, '\n')
2455 else:
2455 else:
2456 ui.write(_('no active guards\n'))
2456 ui.write(_('no active guards\n'))
2457 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2457 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2458 popped = False
2458 popped = False
2459 if opts['pop'] or opts['reapply']:
2459 if opts['pop'] or opts['reapply']:
2460 for i in xrange(len(q.applied)):
2460 for i in xrange(len(q.applied)):
2461 pushable, reason = q.pushable(i)
2461 pushable, reason = q.pushable(i)
2462 if not pushable:
2462 if not pushable:
2463 ui.status(_('popping guarded patches\n'))
2463 ui.status(_('popping guarded patches\n'))
2464 popped = True
2464 popped = True
2465 if i == 0:
2465 if i == 0:
2466 q.pop(repo, all=True)
2466 q.pop(repo, all=True)
2467 else:
2467 else:
2468 q.pop(repo, i - 1)
2468 q.pop(repo, i - 1)
2469 break
2469 break
2470 if popped:
2470 if popped:
2471 try:
2471 try:
2472 if reapply:
2472 if reapply:
2473 ui.status(_('reapplying unguarded patches\n'))
2473 ui.status(_('reapplying unguarded patches\n'))
2474 q.push(repo, reapply)
2474 q.push(repo, reapply)
2475 finally:
2475 finally:
2476 q.save_dirty()
2476 q.save_dirty()
2477
2477
2478 def finish(ui, repo, *revrange, **opts):
2478 def finish(ui, repo, *revrange, **opts):
2479 """move applied patches into repository history
2479 """move applied patches into repository history
2480
2480
2481 Finishes the specified revisions (corresponding to applied
2481 Finishes the specified revisions (corresponding to applied
2482 patches) by moving them out of mq control into regular repository
2482 patches) by moving them out of mq control into regular repository
2483 history.
2483 history.
2484
2484
2485 Accepts a revision range or the -a/--applied option. If --applied
2485 Accepts a revision range or the -a/--applied option. If --applied
2486 is specified, all applied mq revisions are removed from mq
2486 is specified, all applied mq revisions are removed from mq
2487 control. Otherwise, the given revisions must be at the base of the
2487 control. Otherwise, the given revisions must be at the base of the
2488 stack of applied patches.
2488 stack of applied patches.
2489
2489
2490 This can be especially useful if your changes have been applied to
2490 This can be especially useful if your changes have been applied to
2491 an upstream repository, or if you are about to push your changes
2491 an upstream repository, or if you are about to push your changes
2492 to upstream.
2492 to upstream.
2493 """
2493 """
2494 if not opts['applied'] and not revrange:
2494 if not opts['applied'] and not revrange:
2495 raise util.Abort(_('no revisions specified'))
2495 raise util.Abort(_('no revisions specified'))
2496 elif opts['applied']:
2496 elif opts['applied']:
2497 revrange = ('qbase:qtip',) + revrange
2497 revrange = ('qbase:qtip',) + revrange
2498
2498
2499 q = repo.mq
2499 q = repo.mq
2500 if not q.applied:
2500 if not q.applied:
2501 ui.status(_('no patches applied\n'))
2501 ui.status(_('no patches applied\n'))
2502 return 0
2502 return 0
2503
2503
2504 revs = cmdutil.revrange(repo, revrange)
2504 revs = cmdutil.revrange(repo, revrange)
2505 q.finish(repo, revs)
2505 q.finish(repo, revs)
2506 q.save_dirty()
2506 q.save_dirty()
2507 return 0
2507 return 0
2508
2508
2509 def reposetup(ui, repo):
2509 def reposetup(ui, repo):
2510 class mqrepo(repo.__class__):
2510 class mqrepo(repo.__class__):
2511 @util.propertycache
2511 @util.propertycache
2512 def mq(self):
2512 def mq(self):
2513 return queue(self.ui, self.join(""))
2513 return queue(self.ui, self.join(""))
2514
2514
2515 def abort_if_wdir_patched(self, errmsg, force=False):
2515 def abort_if_wdir_patched(self, errmsg, force=False):
2516 if self.mq.applied and not force:
2516 if self.mq.applied and not force:
2517 parent = hex(self.dirstate.parents()[0])
2517 parent = hex(self.dirstate.parents()[0])
2518 if parent in [s.rev for s in self.mq.applied]:
2518 if parent in [s.rev for s in self.mq.applied]:
2519 raise util.Abort(errmsg)
2519 raise util.Abort(errmsg)
2520
2520
2521 def commit(self, text="", user=None, date=None, match=None,
2521 def commit(self, text="", user=None, date=None, match=None,
2522 force=False, editor=False, extra={}):
2522 force=False, editor=False, extra={}):
2523 self.abort_if_wdir_patched(
2523 self.abort_if_wdir_patched(
2524 _('cannot commit over an applied mq patch'),
2524 _('cannot commit over an applied mq patch'),
2525 force)
2525 force)
2526
2526
2527 return super(mqrepo, self).commit(text, user, date, match, force,
2527 return super(mqrepo, self).commit(text, user, date, match, force,
2528 editor, extra)
2528 editor, extra)
2529
2529
2530 def push(self, remote, force=False, revs=None):
2530 def push(self, remote, force=False, revs=None):
2531 if self.mq.applied and not force and not revs:
2531 if self.mq.applied and not force and not revs:
2532 raise util.Abort(_('source has mq patches applied'))
2532 raise util.Abort(_('source has mq patches applied'))
2533 return super(mqrepo, self).push(remote, force, revs)
2533 return super(mqrepo, self).push(remote, force, revs)
2534
2534
2535 def _findtags(self):
2535 def _findtags(self):
2536 '''augment tags from base class with patch tags'''
2536 '''augment tags from base class with patch tags'''
2537 result = super(mqrepo, self)._findtags()
2537 result = super(mqrepo, self)._findtags()
2538
2538
2539 q = self.mq
2539 q = self.mq
2540 if not q.applied:
2540 if not q.applied:
2541 return result
2541 return result
2542
2542
2543 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2543 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2544
2544
2545 if mqtags[-1][0] not in self.changelog.nodemap:
2545 if mqtags[-1][0] not in self.changelog.nodemap:
2546 self.ui.warn(_('mq status file refers to unknown node %s\n')
2546 self.ui.warn(_('mq status file refers to unknown node %s\n')
2547 % short(mqtags[-1][0]))
2547 % short(mqtags[-1][0]))
2548 return result
2548 return result
2549
2549
2550 mqtags.append((mqtags[-1][0], 'qtip'))
2550 mqtags.append((mqtags[-1][0], 'qtip'))
2551 mqtags.append((mqtags[0][0], 'qbase'))
2551 mqtags.append((mqtags[0][0], 'qbase'))
2552 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2552 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2553 tags = result[0]
2553 tags = result[0]
2554 for patch in mqtags:
2554 for patch in mqtags:
2555 if patch[1] in tags:
2555 if patch[1] in tags:
2556 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2556 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2557 % patch[1])
2557 % patch[1])
2558 else:
2558 else:
2559 tags[patch[1]] = patch[0]
2559 tags[patch[1]] = patch[0]
2560
2560
2561 return result
2561 return result
2562
2562
2563 def _branchtags(self, partial, lrev):
2563 def _branchtags(self, partial, lrev):
2564 q = self.mq
2564 q = self.mq
2565 if not q.applied:
2565 if not q.applied:
2566 return super(mqrepo, self)._branchtags(partial, lrev)
2566 return super(mqrepo, self)._branchtags(partial, lrev)
2567
2567
2568 cl = self.changelog
2568 cl = self.changelog
2569 qbasenode = bin(q.applied[0].rev)
2569 qbasenode = bin(q.applied[0].rev)
2570 if qbasenode not in cl.nodemap:
2570 if qbasenode not in cl.nodemap:
2571 self.ui.warn(_('mq status file refers to unknown node %s\n')
2571 self.ui.warn(_('mq status file refers to unknown node %s\n')
2572 % short(qbasenode))
2572 % short(qbasenode))
2573 return super(mqrepo, self)._branchtags(partial, lrev)
2573 return super(mqrepo, self)._branchtags(partial, lrev)
2574
2574
2575 qbase = cl.rev(qbasenode)
2575 qbase = cl.rev(qbasenode)
2576 start = lrev + 1
2576 start = lrev + 1
2577 if start < qbase:
2577 if start < qbase:
2578 # update the cache (excluding the patches) and save it
2578 # update the cache (excluding the patches) and save it
2579 self._updatebranchcache(partial, lrev + 1, qbase)
2579 self._updatebranchcache(partial, lrev + 1, qbase)
2580 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2580 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2581 start = qbase
2581 start = qbase
2582 # if start = qbase, the cache is as updated as it should be.
2582 # if start = qbase, the cache is as updated as it should be.
2583 # if start > qbase, the cache includes (part of) the patches.
2583 # if start > qbase, the cache includes (part of) the patches.
2584 # we might as well use it, but we won't save it.
2584 # we might as well use it, but we won't save it.
2585
2585
2586 # update the cache up to the tip
2586 # update the cache up to the tip
2587 self._updatebranchcache(partial, start, len(cl))
2587 self._updatebranchcache(partial, start, len(cl))
2588
2588
2589 return partial
2589 return partial
2590
2590
2591 if repo.local():
2591 if repo.local():
2592 repo.__class__ = mqrepo
2592 repo.__class__ = mqrepo
2593
2593
2594 def mqimport(orig, ui, repo, *args, **kwargs):
2594 def mqimport(orig, ui, repo, *args, **kwargs):
2595 if (hasattr(repo, 'abort_if_wdir_patched')
2595 if (hasattr(repo, 'abort_if_wdir_patched')
2596 and not kwargs.get('no_commit', False)):
2596 and not kwargs.get('no_commit', False)):
2597 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2597 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2598 kwargs.get('force'))
2598 kwargs.get('force'))
2599 return orig(ui, repo, *args, **kwargs)
2599 return orig(ui, repo, *args, **kwargs)
2600
2600
2601 def mqcommand(orig, ui, repo, *args, **kwargs):
2601 def mqcommand(orig, ui, repo, *args, **kwargs):
2602 """Add --mq option to operate on patch repository instead of main"""
2602 """Add --mq option to operate on patch repository instead of main"""
2603
2603
2604 # some commands do not like getting unknown options
2604 # some commands do not like getting unknown options
2605 mq = kwargs['mq']
2605 mq = kwargs['mq']
2606 del kwargs['mq']
2606 del kwargs['mq']
2607
2607
2608 if not mq:
2608 if not mq:
2609 return orig(ui, repo, *args, **kwargs)
2609 return orig(ui, repo, *args, **kwargs)
2610
2610
2611 q = repo.mq
2611 q = repo.mq
2612 r = q.qrepo()
2612 r = q.qrepo()
2613 if not r:
2613 if not r:
2614 raise util.Abort('no queue repository')
2614 raise util.Abort('no queue repository')
2615 return orig(ui, r, *args, **kwargs)
2615 return orig(ui, r, *args, **kwargs)
2616
2616
2617 def uisetup(ui):
2617 def uisetup(ui):
2618 extensions.wrapcommand(commands.table, 'import', mqimport)
2618 extensions.wrapcommand(commands.table, 'import', mqimport)
2619 for cmd in ('commit', 'export', 'incoming', 'log', 'outgoing', 'pull',
2619 for cmd in ('commit', 'export', 'incoming', 'log', 'outgoing', 'pull',
2620 'push', 'status', 'tag', 'tags', 'tip', 'update'):
2620 'push', 'status', 'tag', 'tags', 'tip', 'update'):
2621 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2621 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2622 entry[1].extend([('Q', 'mq', None, _("operate on patch repository"))])
2622 entry[1].extend([('Q', 'mq', None, _("operate on patch repository"))])
2623
2623
2624 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2624 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2625
2625
2626 cmdtable = {
2626 cmdtable = {
2627 "qapplied":
2627 "qapplied":
2628 (applied,
2628 (applied,
2629 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2629 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2630 _('hg qapplied [-1] [-s] [PATCH]')),
2630 _('hg qapplied [-1] [-s] [PATCH]')),
2631 "qclone":
2631 "qclone":
2632 (clone,
2632 (clone,
2633 [('', 'pull', None, _('use pull protocol to copy metadata')),
2633 [('', 'pull', None, _('use pull protocol to copy metadata')),
2634 ('U', 'noupdate', None, _('do not update the new working directories')),
2634 ('U', 'noupdate', None, _('do not update the new working directories')),
2635 ('', 'uncompressed', None,
2635 ('', 'uncompressed', None,
2636 _('use uncompressed transfer (fast over LAN)')),
2636 _('use uncompressed transfer (fast over LAN)')),
2637 ('p', 'patches', '', _('location of source patch repository')),
2637 ('p', 'patches', '', _('location of source patch repository')),
2638 ] + commands.remoteopts,
2638 ] + commands.remoteopts,
2639 _('hg qclone [OPTION]... SOURCE [DEST]')),
2639 _('hg qclone [OPTION]... SOURCE [DEST]')),
2640 "qcommit|qci":
2640 "qcommit|qci":
2641 (commit,
2641 (commit,
2642 commands.table["^commit|ci"][1],
2642 commands.table["^commit|ci"][1],
2643 _('hg qcommit [OPTION]... [FILE]...')),
2643 _('hg qcommit [OPTION]... [FILE]...')),
2644 "^qdiff":
2644 "^qdiff":
2645 (diff,
2645 (diff,
2646 commands.diffopts + commands.diffopts2 + commands.walkopts,
2646 commands.diffopts + commands.diffopts2 + commands.walkopts,
2647 _('hg qdiff [OPTION]... [FILE]...')),
2647 _('hg qdiff [OPTION]... [FILE]...')),
2648 "qdelete|qremove|qrm":
2648 "qdelete|qremove|qrm":
2649 (delete,
2649 (delete,
2650 [('k', 'keep', None, _('keep patch file')),
2650 [('k', 'keep', None, _('keep patch file')),
2651 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2651 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2652 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2652 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2653 'qfold':
2653 'qfold':
2654 (fold,
2654 (fold,
2655 [('e', 'edit', None, _('edit patch header')),
2655 [('e', 'edit', None, _('edit patch header')),
2656 ('k', 'keep', None, _('keep folded patch files')),
2656 ('k', 'keep', None, _('keep folded patch files')),
2657 ] + commands.commitopts,
2657 ] + commands.commitopts,
2658 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2658 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2659 'qgoto':
2659 'qgoto':
2660 (goto,
2660 (goto,
2661 [('f', 'force', None, _('overwrite any local changes'))],
2661 [('f', 'force', None, _('overwrite any local changes'))],
2662 _('hg qgoto [OPTION]... PATCH')),
2662 _('hg qgoto [OPTION]... PATCH')),
2663 'qguard':
2663 'qguard':
2664 (guard,
2664 (guard,
2665 [('l', 'list', None, _('list all patches and guards')),
2665 [('l', 'list', None, _('list all patches and guards')),
2666 ('n', 'none', None, _('drop all guards'))],
2666 ('n', 'none', None, _('drop all guards'))],
2667 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2667 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2668 'qheader': (header, [], _('hg qheader [PATCH]')),
2668 'qheader': (header, [], _('hg qheader [PATCH]')),
2669 "^qimport":
2669 "^qimport":
2670 (qimport,
2670 (qimport,
2671 [('e', 'existing', None, _('import file in patch directory')),
2671 [('e', 'existing', None, _('import file in patch directory')),
2672 ('n', 'name', '', _('name of patch file')),
2672 ('n', 'name', '', _('name of patch file')),
2673 ('f', 'force', None, _('overwrite existing files')),
2673 ('f', 'force', None, _('overwrite existing files')),
2674 ('r', 'rev', [], _('place existing revisions under mq control')),
2674 ('r', 'rev', [], _('place existing revisions under mq control')),
2675 ('g', 'git', None, _('use git extended diff format')),
2675 ('g', 'git', None, _('use git extended diff format')),
2676 ('P', 'push', None, _('qpush after importing'))],
2676 ('P', 'push', None, _('qpush after importing'))],
2677 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2677 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2678 "^qinit":
2678 "^qinit":
2679 (init,
2679 (init,
2680 [('c', 'create-repo', None, _('create queue repository'))],
2680 [('c', 'create-repo', None, _('create queue repository'))],
2681 _('hg qinit [-c]')),
2681 _('hg qinit [-c]')),
2682 "qnew":
2682 "qnew":
2683 (new,
2683 (new,
2684 [('e', 'edit', None, _('edit commit message')),
2684 [('e', 'edit', None, _('edit commit message')),
2685 ('f', 'force', None, _('import uncommitted changes into patch')),
2685 ('f', 'force', None, _('import uncommitted changes into patch')),
2686 ('g', 'git', None, _('use git extended diff format')),
2686 ('g', 'git', None, _('use git extended diff format')),
2687 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2687 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2688 ('u', 'user', '', _('add "From: <given user>" to patch')),
2688 ('u', 'user', '', _('add "From: <given user>" to patch')),
2689 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2689 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2690 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2690 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2691 ] + commands.walkopts + commands.commitopts,
2691 ] + commands.walkopts + commands.commitopts,
2692 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2692 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2693 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2693 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2694 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2694 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2695 "^qpop":
2695 "^qpop":
2696 (pop,
2696 (pop,
2697 [('a', 'all', None, _('pop all patches')),
2697 [('a', 'all', None, _('pop all patches')),
2698 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2698 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2699 ('f', 'force', None, _('forget any local changes to patched files'))],
2699 ('f', 'force', None, _('forget any local changes to patched files'))],
2700 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2700 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2701 "^qpush":
2701 "^qpush":
2702 (push,
2702 (push,
2703 [('f', 'force', None, _('apply if the patch has rejects')),
2703 [('f', 'force', None, _('apply if the patch has rejects')),
2704 ('l', 'list', None, _('list patch name in commit text')),
2704 ('l', 'list', None, _('list patch name in commit text')),
2705 ('a', 'all', None, _('apply all patches')),
2705 ('a', 'all', None, _('apply all patches')),
2706 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2706 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2707 ('n', 'name', '', _('merge queue name (DEPRECATED)'))],
2707 ('n', 'name', '', _('merge queue name (DEPRECATED)'))],
2708 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2708 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2709 "^qrefresh":
2709 "^qrefresh":
2710 (refresh,
2710 (refresh,
2711 [('e', 'edit', None, _('edit commit message')),
2711 [('e', 'edit', None, _('edit commit message')),
2712 ('g', 'git', None, _('use git extended diff format')),
2712 ('g', 'git', None, _('use git extended diff format')),
2713 ('s', 'short', None,
2713 ('s', 'short', None,
2714 _('refresh only files already in the patch and specified files')),
2714 _('refresh only files already in the patch and specified files')),
2715 ('U', 'currentuser', None,
2715 ('U', 'currentuser', None,
2716 _('add/update author field in patch with current user')),
2716 _('add/update author field in patch with current user')),
2717 ('u', 'user', '',
2717 ('u', 'user', '',
2718 _('add/update author field in patch with given user')),
2718 _('add/update author field in patch with given user')),
2719 ('D', 'currentdate', None,
2719 ('D', 'currentdate', None,
2720 _('add/update date field in patch with current date')),
2720 _('add/update date field in patch with current date')),
2721 ('d', 'date', '',
2721 ('d', 'date', '',
2722 _('add/update date field in patch with given date'))
2722 _('add/update date field in patch with given date'))
2723 ] + commands.walkopts + commands.commitopts,
2723 ] + commands.walkopts + commands.commitopts,
2724 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2724 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2725 'qrename|qmv':
2725 'qrename|qmv':
2726 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2726 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2727 "qrestore":
2727 "qrestore":
2728 (restore,
2728 (restore,
2729 [('d', 'delete', None, _('delete save entry')),
2729 [('d', 'delete', None, _('delete save entry')),
2730 ('u', 'update', None, _('update queue working directory'))],
2730 ('u', 'update', None, _('update queue working directory'))],
2731 _('hg qrestore [-d] [-u] REV')),
2731 _('hg qrestore [-d] [-u] REV')),
2732 "qsave":
2732 "qsave":
2733 (save,
2733 (save,
2734 [('c', 'copy', None, _('copy patch directory')),
2734 [('c', 'copy', None, _('copy patch directory')),
2735 ('n', 'name', '', _('copy directory name')),
2735 ('n', 'name', '', _('copy directory name')),
2736 ('e', 'empty', None, _('clear queue status file')),
2736 ('e', 'empty', None, _('clear queue status file')),
2737 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2737 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2738 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2738 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2739 "qselect":
2739 "qselect":
2740 (select,
2740 (select,
2741 [('n', 'none', None, _('disable all guards')),
2741 [('n', 'none', None, _('disable all guards')),
2742 ('s', 'series', None, _('list all guards in series file')),
2742 ('s', 'series', None, _('list all guards in series file')),
2743 ('', 'pop', None, _('pop to before first guarded applied patch')),
2743 ('', 'pop', None, _('pop to before first guarded applied patch')),
2744 ('', 'reapply', None, _('pop, then reapply patches'))],
2744 ('', 'reapply', None, _('pop, then reapply patches'))],
2745 _('hg qselect [OPTION]... [GUARD]...')),
2745 _('hg qselect [OPTION]... [GUARD]...')),
2746 "qseries":
2746 "qseries":
2747 (series,
2747 (series,
2748 [('m', 'missing', None, _('print patches not in series')),
2748 [('m', 'missing', None, _('print patches not in series')),
2749 ] + seriesopts,
2749 ] + seriesopts,
2750 _('hg qseries [-ms]')),
2750 _('hg qseries [-ms]')),
2751 "^strip":
2751 "^strip":
2752 (strip,
2752 (strip,
2753 [('f', 'force', None, _('force removal with local changes')),
2753 [('f', 'force', None, _('force removal with local changes')),
2754 ('b', 'backup', None, _('bundle unrelated changesets')),
2754 ('b', 'backup', None, _('bundle unrelated changesets')),
2755 ('n', 'nobackup', None, _('no backups'))],
2755 ('n', 'nobackup', None, _('no backups'))],
2756 _('hg strip [-f] [-b] [-n] REV')),
2756 _('hg strip [-f] [-b] [-n] REV')),
2757 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2757 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2758 "qunapplied":
2758 "qunapplied":
2759 (unapplied,
2759 (unapplied,
2760 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2760 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2761 _('hg qunapplied [-1] [-s] [PATCH]')),
2761 _('hg qunapplied [-1] [-s] [PATCH]')),
2762 "qfinish":
2762 "qfinish":
2763 (finish,
2763 (finish,
2764 [('a', 'applied', None, _('finish all applied changesets'))],
2764 [('a', 'applied', None, _('finish all applied changesets'))],
2765 _('hg qfinish [-a] [REV]...')),
2765 _('hg qfinish [-a] [REV]...')),
2766 }
2766 }
General Comments 0
You need to be logged in to leave comments. Login now