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