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