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