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