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