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