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