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