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