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