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