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