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