##// END OF EJS Templates
manifest.execf is now a function.
Vadim Gelfer -
r2872:d36af1f1 default
parent child Browse files
Show More
@@ -1,1925 +1,1925 b''
1 1
2 2 # queue.py - patch queues for mercurial
3 3 #
4 4 # Copyright 2005 Chris Mason <mason@suse.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 '''patch management and development
10 10
11 11 This extension lets you work with a stack of patches in a Mercurial
12 12 repository. It manages two stacks of patches - all known patches, and
13 13 applied patches (subset of known patches).
14 14
15 15 Known patches are represented as patch files in the .hg/patches
16 16 directory. Applied patches are both patch files and changesets.
17 17
18 18 Common tasks (use "hg help command" for more details):
19 19
20 20 prepare repository to work with patches qinit
21 21 create new patch qnew
22 22 import existing patch qimport
23 23
24 24 print patch series qseries
25 25 print applied patches qapplied
26 26 print name of top applied patch qtop
27 27
28 28 add known patch to applied stack qpush
29 29 remove patch from applied stack qpop
30 30 refresh contents of top applied patch qrefresh
31 31 '''
32 32
33 33 from mercurial.demandload import *
34 34 demandload(globals(), "os sys re struct traceback errno bz2")
35 35 from mercurial.i18n import gettext as _
36 36 from mercurial import ui, hg, revlog, commands, util
37 37
38 38 commands.norepo += " qclone qversion"
39 39
40 40 class statusentry:
41 41 def __init__(self, rev, name=None):
42 42 if not name:
43 43 fields = rev.split(':')
44 44 if len(fields) == 2:
45 45 self.rev, self.name = fields
46 46 else:
47 47 self.rev, self.name = None, None
48 48 else:
49 49 self.rev, self.name = rev, name
50 50
51 51 def __str__(self):
52 52 return self.rev + ':' + self.name
53 53
54 54 class queue:
55 55 def __init__(self, ui, path, patchdir=None):
56 56 self.basepath = path
57 57 self.path = patchdir or os.path.join(path, "patches")
58 58 self.opener = util.opener(self.path)
59 59 self.ui = ui
60 60 self.applied = []
61 61 self.full_series = []
62 62 self.applied_dirty = 0
63 63 self.series_dirty = 0
64 64 self.series_path = "series"
65 65 self.status_path = "status"
66 66 self.guards_path = "guards"
67 67 self.active_guards = None
68 68 self.guards_dirty = False
69 69
70 70 if os.path.exists(self.join(self.series_path)):
71 71 self.full_series = self.opener(self.series_path).read().splitlines()
72 72 self.parse_series()
73 73
74 74 if os.path.exists(self.join(self.status_path)):
75 75 lines = self.opener(self.status_path).read().splitlines()
76 76 self.applied = [statusentry(l) for l in lines]
77 77
78 78 def join(self, *p):
79 79 return os.path.join(self.path, *p)
80 80
81 81 def find_series(self, patch):
82 82 pre = re.compile("(\s*)([^#]+)")
83 83 index = 0
84 84 for l in self.full_series:
85 85 m = pre.match(l)
86 86 if m:
87 87 s = m.group(2)
88 88 s = s.rstrip()
89 89 if s == patch:
90 90 return index
91 91 index += 1
92 92 return None
93 93
94 94 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
95 95
96 96 def parse_series(self):
97 97 self.series = []
98 98 self.series_guards = []
99 99 for l in self.full_series:
100 100 h = l.find('#')
101 101 if h == -1:
102 102 patch = l
103 103 comment = ''
104 104 elif h == 0:
105 105 continue
106 106 else:
107 107 patch = l[:h]
108 108 comment = l[h:]
109 109 patch = patch.strip()
110 110 if patch:
111 111 self.series.append(patch)
112 112 self.series_guards.append(self.guard_re.findall(comment))
113 113
114 114 def check_guard(self, guard):
115 115 bad_chars = '# \t\r\n\f'
116 116 first = guard[0]
117 117 for c in '-+':
118 118 if first == c:
119 119 return (_('guard %r starts with invalid character: %r') %
120 120 (guard, c))
121 121 for c in bad_chars:
122 122 if c in guard:
123 123 return _('invalid character in guard %r: %r') % (guard, c)
124 124
125 125 def set_active(self, guards):
126 126 for guard in guards:
127 127 bad = self.check_guard(guard)
128 128 if bad:
129 129 raise util.Abort(bad)
130 130 guards = dict.fromkeys(guards).keys()
131 131 guards.sort()
132 132 self.ui.debug('active guards: %s\n' % ' '.join(guards))
133 133 self.active_guards = guards
134 134 self.guards_dirty = True
135 135
136 136 def active(self):
137 137 if self.active_guards is None:
138 138 self.active_guards = []
139 139 try:
140 140 guards = self.opener(self.guards_path).read().split()
141 141 except IOError, err:
142 142 if err.errno != errno.ENOENT: raise
143 143 guards = []
144 144 for i, guard in enumerate(guards):
145 145 bad = self.check_guard(guard)
146 146 if bad:
147 147 self.ui.warn('%s:%d: %s\n' %
148 148 (self.join(self.guards_path), i + 1, bad))
149 149 else:
150 150 self.active_guards.append(guard)
151 151 return self.active_guards
152 152
153 153 def set_guards(self, idx, guards):
154 154 for g in guards:
155 155 if len(g) < 2:
156 156 raise util.Abort(_('guard %r too short') % g)
157 157 if g[0] not in '-+':
158 158 raise util.Abort(_('guard %r starts with invalid char') % g)
159 159 bad = self.check_guard(g[1:])
160 160 if bad:
161 161 raise util.Abort(bad)
162 162 drop = self.guard_re.sub('', self.full_series[idx])
163 163 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
164 164 self.parse_series()
165 165 self.series_dirty = True
166 166
167 167 def pushable(self, idx):
168 168 if isinstance(idx, str):
169 169 idx = self.series.index(idx)
170 170 patchguards = self.series_guards[idx]
171 171 if not patchguards:
172 172 return True, None
173 173 default = False
174 174 guards = self.active()
175 175 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
176 176 if exactneg:
177 177 return False, exactneg[0]
178 178 pos = [g for g in patchguards if g[0] == '+']
179 179 nonpos = [g for g in pos if g[1:] not in guards]
180 180 if pos:
181 181 if not nonpos:
182 182 return True, ''
183 183 return False, nonpos
184 184 return True, ''
185 185
186 186 def explain_pushable(self, idx, all_patches=False):
187 187 write = all_patches and self.ui.write or self.ui.warn
188 188 if all_patches or self.ui.verbose:
189 189 if isinstance(idx, str):
190 190 idx = self.series.index(idx)
191 191 pushable, why = self.pushable(idx)
192 192 if all_patches and pushable:
193 193 if why is None:
194 194 write(_('allowing %s - no guards in effect\n') %
195 195 self.series[idx])
196 196 else:
197 197 if not why:
198 198 write(_('allowing %s - no matching negative guards\n') %
199 199 self.series[idx])
200 200 else:
201 201 write(_('allowing %s - guarded by %r\n') %
202 202 (self.series[idx], why))
203 203 if not pushable:
204 204 if why:
205 205 write(_('skipping %s - guarded by %r\n') %
206 206 (self.series[idx], ' '.join(why)))
207 207 else:
208 208 write(_('skipping %s - no matching guards\n') %
209 209 self.series[idx])
210 210
211 211 def save_dirty(self):
212 212 def write_list(items, path):
213 213 fp = self.opener(path, 'w')
214 214 for i in items:
215 215 print >> fp, i
216 216 fp.close()
217 217 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
218 218 if self.series_dirty: write_list(self.full_series, self.series_path)
219 219 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
220 220
221 221 def readheaders(self, patch):
222 222 def eatdiff(lines):
223 223 while lines:
224 224 l = lines[-1]
225 225 if (l.startswith("diff -") or
226 226 l.startswith("Index:") or
227 227 l.startswith("===========")):
228 228 del lines[-1]
229 229 else:
230 230 break
231 231 def eatempty(lines):
232 232 while lines:
233 233 l = lines[-1]
234 234 if re.match('\s*$', l):
235 235 del lines[-1]
236 236 else:
237 237 break
238 238
239 239 pf = self.join(patch)
240 240 message = []
241 241 comments = []
242 242 user = None
243 243 date = None
244 244 format = None
245 245 subject = None
246 246 diffstart = 0
247 247
248 248 for line in file(pf):
249 249 line = line.rstrip()
250 250 if diffstart:
251 251 if line.startswith('+++ '):
252 252 diffstart = 2
253 253 break
254 254 if line.startswith("--- "):
255 255 diffstart = 1
256 256 continue
257 257 elif format == "hgpatch":
258 258 # parse values when importing the result of an hg export
259 259 if line.startswith("# User "):
260 260 user = line[7:]
261 261 elif line.startswith("# Date "):
262 262 date = line[7:]
263 263 elif not line.startswith("# ") and line:
264 264 message.append(line)
265 265 format = None
266 266 elif line == '# HG changeset patch':
267 267 format = "hgpatch"
268 268 elif (format != "tagdone" and (line.startswith("Subject: ") or
269 269 line.startswith("subject: "))):
270 270 subject = line[9:]
271 271 format = "tag"
272 272 elif (format != "tagdone" and (line.startswith("From: ") or
273 273 line.startswith("from: "))):
274 274 user = line[6:]
275 275 format = "tag"
276 276 elif format == "tag" and line == "":
277 277 # when looking for tags (subject: from: etc) they
278 278 # end once you find a blank line in the source
279 279 format = "tagdone"
280 280 elif message or line:
281 281 message.append(line)
282 282 comments.append(line)
283 283
284 284 eatdiff(message)
285 285 eatdiff(comments)
286 286 eatempty(message)
287 287 eatempty(comments)
288 288
289 289 # make sure message isn't empty
290 290 if format and format.startswith("tag") and subject:
291 291 message.insert(0, "")
292 292 message.insert(0, subject)
293 293 return (message, comments, user, date, diffstart > 1)
294 294
295 295 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
296 296 # first try just applying the patch
297 297 (err, n) = self.apply(repo, [ patch ], update_status=False,
298 298 strict=True, merge=rev, wlock=wlock)
299 299
300 300 if err == 0:
301 301 return (err, n)
302 302
303 303 if n is None:
304 304 raise util.Abort(_("apply failed for patch %s") % patch)
305 305
306 306 self.ui.warn("patch didn't work out, merging %s\n" % patch)
307 307
308 308 # apply failed, strip away that rev and merge.
309 309 hg.clean(repo, head, wlock=wlock)
310 310 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
311 311
312 312 c = repo.changelog.read(rev)
313 313 ret = hg.merge(repo, rev, wlock=wlock)
314 314 if ret:
315 315 raise util.Abort(_("update returned %d") % ret)
316 316 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
317 317 if n == None:
318 318 raise util.Abort(_("repo commit failed"))
319 319 try:
320 320 message, comments, user, date, patchfound = mergeq.readheaders(patch)
321 321 except:
322 322 raise util.Abort(_("unable to read %s") % patch)
323 323
324 324 patchf = self.opener(patch, "w")
325 325 if comments:
326 326 comments = "\n".join(comments) + '\n\n'
327 327 patchf.write(comments)
328 328 commands.dodiff(patchf, self.ui, repo, head, n)
329 329 patchf.close()
330 330 return (0, n)
331 331
332 332 def qparents(self, repo, rev=None):
333 333 if rev is None:
334 334 (p1, p2) = repo.dirstate.parents()
335 335 if p2 == revlog.nullid:
336 336 return p1
337 337 if len(self.applied) == 0:
338 338 return None
339 339 return revlog.bin(self.applied[-1].rev)
340 340 pp = repo.changelog.parents(rev)
341 341 if pp[1] != revlog.nullid:
342 342 arevs = [ x.rev for x in self.applied ]
343 343 p0 = revlog.hex(pp[0])
344 344 p1 = revlog.hex(pp[1])
345 345 if p0 in arevs:
346 346 return pp[0]
347 347 if p1 in arevs:
348 348 return pp[1]
349 349 return pp[0]
350 350
351 351 def mergepatch(self, repo, mergeq, series, wlock):
352 352 if len(self.applied) == 0:
353 353 # each of the patches merged in will have two parents. This
354 354 # can confuse the qrefresh, qdiff, and strip code because it
355 355 # needs to know which parent is actually in the patch queue.
356 356 # so, we insert a merge marker with only one parent. This way
357 357 # the first patch in the queue is never a merge patch
358 358 #
359 359 pname = ".hg.patches.merge.marker"
360 360 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
361 361 wlock=wlock)
362 362 self.applied.append(statusentry(revlog.hex(n), pname))
363 363 self.applied_dirty = 1
364 364
365 365 head = self.qparents(repo)
366 366
367 367 for patch in series:
368 368 patch = mergeq.lookup(patch, strict=True)
369 369 if not patch:
370 370 self.ui.warn("patch %s does not exist\n" % patch)
371 371 return (1, None)
372 372 pushable, reason = self.pushable(patch)
373 373 if not pushable:
374 374 self.explain_pushable(patch, all_patches=True)
375 375 continue
376 376 info = mergeq.isapplied(patch)
377 377 if not info:
378 378 self.ui.warn("patch %s is not applied\n" % patch)
379 379 return (1, None)
380 380 rev = revlog.bin(info[1])
381 381 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
382 382 if head:
383 383 self.applied.append(statusentry(revlog.hex(head), patch))
384 384 self.applied_dirty = 1
385 385 if err:
386 386 return (err, head)
387 387 return (0, head)
388 388
389 389 def patch(self, repo, patchfile):
390 390 '''Apply patchfile to the working directory.
391 391 patchfile: file name of patch'''
392 392 try:
393 393 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
394 394 f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
395 395 (pp, util.shellquote(repo.root), util.shellquote(patchfile)))
396 396 except:
397 397 self.ui.warn("patch failed, unable to continue (try -v)\n")
398 398 return (None, [], False)
399 399 files = []
400 400 fuzz = False
401 401 for l in f:
402 402 l = l.rstrip('\r\n');
403 403 if self.ui.verbose:
404 404 self.ui.warn(l + "\n")
405 405 if l[:14] == 'patching file ':
406 406 pf = os.path.normpath(util.parse_patch_output(l))
407 407 if pf not in files:
408 408 files.append(pf)
409 409 printed_file = False
410 410 file_str = l
411 411 elif l.find('with fuzz') >= 0:
412 412 if not printed_file:
413 413 self.ui.warn(file_str + '\n')
414 414 printed_file = True
415 415 self.ui.warn(l + '\n')
416 416 fuzz = True
417 417 elif l.find('saving rejects to file') >= 0:
418 418 self.ui.warn(l + '\n')
419 419 elif l.find('FAILED') >= 0:
420 420 if not printed_file:
421 421 self.ui.warn(file_str + '\n')
422 422 printed_file = True
423 423 self.ui.warn(l + '\n')
424 424
425 425 return (not f.close(), files, fuzz)
426 426
427 427 def apply(self, repo, series, list=False, update_status=True,
428 428 strict=False, patchdir=None, merge=None, wlock=None):
429 429 # TODO unify with commands.py
430 430 if not patchdir:
431 431 patchdir = self.path
432 432 err = 0
433 433 if not wlock:
434 434 wlock = repo.wlock()
435 435 lock = repo.lock()
436 436 tr = repo.transaction()
437 437 n = None
438 438 for patch in series:
439 439 pushable, reason = self.pushable(patch)
440 440 if not pushable:
441 441 self.explain_pushable(patch, all_patches=True)
442 442 continue
443 443 self.ui.warn("applying %s\n" % patch)
444 444 pf = os.path.join(patchdir, patch)
445 445
446 446 try:
447 447 message, comments, user, date, patchfound = self.readheaders(patch)
448 448 except:
449 449 self.ui.warn("Unable to read %s\n" % pf)
450 450 err = 1
451 451 break
452 452
453 453 if not message:
454 454 message = "imported patch %s\n" % patch
455 455 else:
456 456 if list:
457 457 message.append("\nimported patch %s" % patch)
458 458 message = '\n'.join(message)
459 459
460 460 (patcherr, files, fuzz) = self.patch(repo, pf)
461 461 patcherr = not patcherr
462 462
463 463 if merge and len(files) > 0:
464 464 # Mark as merged and update dirstate parent info
465 465 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
466 466 p1, p2 = repo.dirstate.parents()
467 467 repo.dirstate.setparents(p1, merge)
468 468 if len(files) > 0:
469 469 cwd = repo.getcwd()
470 470 cfiles = files
471 471 if cwd:
472 472 cfiles = [util.pathto(cwd, f) for f in files]
473 473 commands.addremove_lock(self.ui, repo, cfiles,
474 474 opts={}, wlock=wlock)
475 475 n = repo.commit(files, message, user, date, force=1, lock=lock,
476 476 wlock=wlock)
477 477
478 478 if n == None:
479 479 raise util.Abort(_("repo commit failed"))
480 480
481 481 if update_status:
482 482 self.applied.append(statusentry(revlog.hex(n), patch))
483 483
484 484 if patcherr:
485 485 if not patchfound:
486 486 self.ui.warn("patch %s is empty\n" % patch)
487 487 err = 0
488 488 else:
489 489 self.ui.warn("patch failed, rejects left in working dir\n")
490 490 err = 1
491 491 break
492 492
493 493 if fuzz and strict:
494 494 self.ui.warn("fuzz found when applying patch, stopping\n")
495 495 err = 1
496 496 break
497 497 tr.close()
498 498 return (err, n)
499 499
500 500 def delete(self, repo, patch, force=False):
501 501 patch = self.lookup(patch, strict=True)
502 502 info = self.isapplied(patch)
503 503 if info:
504 504 raise util.Abort(_("cannot delete applied patch %s") % patch)
505 505 if patch not in self.series:
506 506 raise util.Abort(_("patch %s not in series file") % patch)
507 507 if force:
508 508 r = self.qrepo()
509 509 if r:
510 510 r.remove([patch], True)
511 511 else:
512 512 os.unlink(self.join(patch))
513 513 i = self.find_series(patch)
514 514 del self.full_series[i]
515 515 self.parse_series()
516 516 self.series_dirty = 1
517 517
518 518 def check_toppatch(self, repo):
519 519 if len(self.applied) > 0:
520 520 top = revlog.bin(self.applied[-1].rev)
521 521 pp = repo.dirstate.parents()
522 522 if top not in pp:
523 523 raise util.Abort(_("queue top not at same revision as working directory"))
524 524 return top
525 525 return None
526 526 def check_localchanges(self, repo):
527 527 (c, a, r, d, u) = repo.changes(None, None)
528 528 if c or a or d or r:
529 529 raise util.Abort(_("local changes found, refresh first"))
530 530 def new(self, repo, patch, msg=None, force=None):
531 531 if os.path.exists(self.join(patch)):
532 532 raise util.Abort(_('patch "%s" already exists') % patch)
533 533 commitfiles = []
534 534 (c, a, r, d, u) = repo.changes(None, None)
535 535 if c or a or d or r:
536 536 if not force:
537 537 raise util.Abort(_("local changes found, refresh first"))
538 538 commitfiles = c + a + r
539 539 self.check_toppatch(repo)
540 540 wlock = repo.wlock()
541 541 insert = self.full_series_end()
542 542 if msg:
543 543 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
544 544 wlock=wlock)
545 545 else:
546 546 n = repo.commit(commitfiles,
547 547 "New patch: %s" % patch, force=True, wlock=wlock)
548 548 if n == None:
549 549 raise util.Abort(_("repo commit failed"))
550 550 self.full_series[insert:insert] = [patch]
551 551 self.applied.append(statusentry(revlog.hex(n), patch))
552 552 self.parse_series()
553 553 self.series_dirty = 1
554 554 self.applied_dirty = 1
555 555 p = self.opener(patch, "w")
556 556 if msg:
557 557 msg = msg + "\n"
558 558 p.write(msg)
559 559 p.close()
560 560 wlock = None
561 561 r = self.qrepo()
562 562 if r: r.add([patch])
563 563 if commitfiles:
564 564 self.refresh(repo, msg=None, short=True)
565 565
566 566 def strip(self, repo, rev, update=True, backup="all", wlock=None):
567 567 def limitheads(chlog, stop):
568 568 """return the list of all nodes that have no children"""
569 569 p = {}
570 570 h = []
571 571 stoprev = 0
572 572 if stop in chlog.nodemap:
573 573 stoprev = chlog.rev(stop)
574 574
575 575 for r in range(chlog.count() - 1, -1, -1):
576 576 n = chlog.node(r)
577 577 if n not in p:
578 578 h.append(n)
579 579 if n == stop:
580 580 break
581 581 if r < stoprev:
582 582 break
583 583 for pn in chlog.parents(n):
584 584 p[pn] = 1
585 585 return h
586 586
587 587 def bundle(cg):
588 588 backupdir = repo.join("strip-backup")
589 589 if not os.path.isdir(backupdir):
590 590 os.mkdir(backupdir)
591 591 name = os.path.join(backupdir, "%s" % revlog.short(rev))
592 592 name = savename(name)
593 593 self.ui.warn("saving bundle to %s\n" % name)
594 594 # TODO, exclusive open
595 595 f = open(name, "wb")
596 596 try:
597 597 f.write("HG10")
598 598 z = bz2.BZ2Compressor(9)
599 599 while 1:
600 600 chunk = cg.read(4096)
601 601 if not chunk:
602 602 break
603 603 f.write(z.compress(chunk))
604 604 f.write(z.flush())
605 605 except:
606 606 os.unlink(name)
607 607 raise
608 608 f.close()
609 609 return name
610 610
611 611 def stripall(rev, revnum):
612 612 cl = repo.changelog
613 613 c = cl.read(rev)
614 614 mm = repo.manifest.read(c[0])
615 615 seen = {}
616 616
617 617 for x in xrange(revnum, cl.count()):
618 618 c = cl.read(cl.node(x))
619 619 for f in c[3]:
620 620 if f in seen:
621 621 continue
622 622 seen[f] = 1
623 623 if f in mm:
624 624 filerev = mm[f]
625 625 else:
626 626 filerev = 0
627 627 seen[f] = filerev
628 628 # we go in two steps here so the strip loop happens in a
629 629 # sensible order. When stripping many files, this helps keep
630 630 # our disk access patterns under control.
631 631 seen_list = seen.keys()
632 632 seen_list.sort()
633 633 for f in seen_list:
634 634 ff = repo.file(f)
635 635 filerev = seen[f]
636 636 if filerev != 0:
637 637 if filerev in ff.nodemap:
638 638 filerev = ff.rev(filerev)
639 639 else:
640 640 filerev = 0
641 641 ff.strip(filerev, revnum)
642 642
643 643 if not wlock:
644 644 wlock = repo.wlock()
645 645 lock = repo.lock()
646 646 chlog = repo.changelog
647 647 # TODO delete the undo files, and handle undo of merge sets
648 648 pp = chlog.parents(rev)
649 649 revnum = chlog.rev(rev)
650 650
651 651 if update:
652 652 (c, a, r, d, u) = repo.changes(None, None)
653 653 if c or a or d or r:
654 654 raise util.Abort(_("local changes found"))
655 655 urev = self.qparents(repo, rev)
656 656 hg.clean(repo, urev, wlock=wlock)
657 657 repo.dirstate.write()
658 658
659 659 # save is a list of all the branches we are truncating away
660 660 # that we actually want to keep. changegroup will be used
661 661 # to preserve them and add them back after the truncate
662 662 saveheads = []
663 663 savebases = {}
664 664
665 665 heads = limitheads(chlog, rev)
666 666 seen = {}
667 667
668 668 # search through all the heads, finding those where the revision
669 669 # we want to strip away is an ancestor. Also look for merges
670 670 # that might be turned into new heads by the strip.
671 671 while heads:
672 672 h = heads.pop()
673 673 n = h
674 674 while True:
675 675 seen[n] = 1
676 676 pp = chlog.parents(n)
677 677 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
678 678 if pp[1] not in seen:
679 679 heads.append(pp[1])
680 680 if pp[0] == revlog.nullid:
681 681 break
682 682 if chlog.rev(pp[0]) < revnum:
683 683 break
684 684 n = pp[0]
685 685 if n == rev:
686 686 break
687 687 r = chlog.reachable(h, rev)
688 688 if rev not in r:
689 689 saveheads.append(h)
690 690 for x in r:
691 691 if chlog.rev(x) > revnum:
692 692 savebases[x] = 1
693 693
694 694 # create a changegroup for all the branches we need to keep
695 695 if backup == "all":
696 696 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
697 697 bundle(backupch)
698 698 if saveheads:
699 699 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
700 700 chgrpfile = bundle(backupch)
701 701
702 702 stripall(rev, revnum)
703 703
704 704 change = chlog.read(rev)
705 705 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
706 706 chlog.strip(revnum, revnum)
707 707 if saveheads:
708 708 self.ui.status("adding branch\n")
709 709 commands.unbundle(self.ui, repo, chgrpfile, update=False)
710 710 if backup != "strip":
711 711 os.unlink(chgrpfile)
712 712
713 713 def isapplied(self, patch):
714 714 """returns (index, rev, patch)"""
715 715 for i in xrange(len(self.applied)):
716 716 a = self.applied[i]
717 717 if a.name == patch:
718 718 return (i, a.rev, a.name)
719 719 return None
720 720
721 721 # if the exact patch name does not exist, we try a few
722 722 # variations. If strict is passed, we try only #1
723 723 #
724 724 # 1) a number to indicate an offset in the series file
725 725 # 2) a unique substring of the patch name was given
726 726 # 3) patchname[-+]num to indicate an offset in the series file
727 727 def lookup(self, patch, strict=False):
728 728 def partial_name(s):
729 729 if s in self.series:
730 730 return s
731 731 matches = [x for x in self.series if s in x]
732 732 if len(matches) > 1:
733 733 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
734 734 for m in matches:
735 735 self.ui.warn(' %s\n' % m)
736 736 return None
737 737 if matches:
738 738 return matches[0]
739 739 if len(self.series) > 0 and len(self.applied) > 0:
740 740 if s == 'qtip':
741 741 return self.series[self.series_end()-1]
742 742 if s == 'qbase':
743 743 return self.series[0]
744 744 return None
745 745 if patch == None:
746 746 return None
747 747
748 748 # we don't want to return a partial match until we make
749 749 # sure the file name passed in does not exist (checked below)
750 750 res = partial_name(patch)
751 751 if res and res == patch:
752 752 return res
753 753
754 754 if not os.path.isfile(self.join(patch)):
755 755 try:
756 756 sno = int(patch)
757 757 except(ValueError, OverflowError):
758 758 pass
759 759 else:
760 760 if sno < len(self.series):
761 761 return self.series[sno]
762 762 if not strict:
763 763 # return any partial match made above
764 764 if res:
765 765 return res
766 766 minus = patch.rsplit('-', 1)
767 767 if len(minus) > 1:
768 768 res = partial_name(minus[0])
769 769 if res:
770 770 i = self.series.index(res)
771 771 try:
772 772 off = int(minus[1] or 1)
773 773 except(ValueError, OverflowError):
774 774 pass
775 775 else:
776 776 if i - off >= 0:
777 777 return self.series[i - off]
778 778 plus = patch.rsplit('+', 1)
779 779 if len(plus) > 1:
780 780 res = partial_name(plus[0])
781 781 if res:
782 782 i = self.series.index(res)
783 783 try:
784 784 off = int(plus[1] or 1)
785 785 except(ValueError, OverflowError):
786 786 pass
787 787 else:
788 788 if i + off < len(self.series):
789 789 return self.series[i + off]
790 790 raise util.Abort(_("patch %s not in series") % patch)
791 791
792 792 def push(self, repo, patch=None, force=False, list=False,
793 793 mergeq=None, wlock=None):
794 794 if not wlock:
795 795 wlock = repo.wlock()
796 796 patch = self.lookup(patch)
797 797 if patch and self.isapplied(patch):
798 798 self.ui.warn(_("patch %s is already applied\n") % patch)
799 799 sys.exit(1)
800 800 if self.series_end() == len(self.series):
801 801 self.ui.warn(_("patch series fully applied\n"))
802 802 sys.exit(1)
803 803 if not force:
804 804 self.check_localchanges(repo)
805 805
806 806 self.applied_dirty = 1;
807 807 start = self.series_end()
808 808 if start > 0:
809 809 self.check_toppatch(repo)
810 810 if not patch:
811 811 patch = self.series[start]
812 812 end = start + 1
813 813 else:
814 814 end = self.series.index(patch, start) + 1
815 815 s = self.series[start:end]
816 816 if mergeq:
817 817 ret = self.mergepatch(repo, mergeq, s, wlock)
818 818 else:
819 819 ret = self.apply(repo, s, list, wlock=wlock)
820 820 top = self.applied[-1].name
821 821 if ret[0]:
822 822 self.ui.write("Errors during apply, please fix and refresh %s\n" %
823 823 top)
824 824 else:
825 825 self.ui.write("Now at: %s\n" % top)
826 826 return ret[0]
827 827
828 828 def pop(self, repo, patch=None, force=False, update=True, all=False,
829 829 wlock=None):
830 830 def getfile(f, rev):
831 831 t = repo.file(f).read(rev)
832 832 try:
833 833 repo.wfile(f, "w").write(t)
834 834 except IOError:
835 835 try:
836 836 os.makedirs(os.path.dirname(repo.wjoin(f)))
837 837 except OSError, err:
838 838 if err.errno != errno.EEXIST: raise
839 839 repo.wfile(f, "w").write(t)
840 840
841 841 if not wlock:
842 842 wlock = repo.wlock()
843 843 if patch:
844 844 # index, rev, patch
845 845 info = self.isapplied(patch)
846 846 if not info:
847 847 patch = self.lookup(patch)
848 848 info = self.isapplied(patch)
849 849 if not info:
850 850 raise util.Abort(_("patch %s is not applied") % patch)
851 851 if len(self.applied) == 0:
852 852 self.ui.warn(_("no patches applied\n"))
853 853 sys.exit(1)
854 854
855 855 if not update:
856 856 parents = repo.dirstate.parents()
857 857 rr = [ revlog.bin(x.rev) for x in self.applied ]
858 858 for p in parents:
859 859 if p in rr:
860 860 self.ui.warn("qpop: forcing dirstate update\n")
861 861 update = True
862 862
863 863 if not force and update:
864 864 self.check_localchanges(repo)
865 865
866 866 self.applied_dirty = 1;
867 867 end = len(self.applied)
868 868 if not patch:
869 869 if all:
870 870 popi = 0
871 871 else:
872 872 popi = len(self.applied) - 1
873 873 else:
874 874 popi = info[0] + 1
875 875 if popi >= end:
876 876 self.ui.warn("qpop: %s is already at the top\n" % patch)
877 877 return
878 878 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
879 879
880 880 start = info[0]
881 881 rev = revlog.bin(info[1])
882 882
883 883 # we know there are no local changes, so we can make a simplified
884 884 # form of hg.update.
885 885 if update:
886 886 top = self.check_toppatch(repo)
887 887 qp = self.qparents(repo, rev)
888 888 changes = repo.changelog.read(qp)
889 889 mmap = repo.manifest.read(changes[0])
890 890 (c, a, r, d, u) = repo.changes(qp, top)
891 891 if d:
892 892 raise util.Abort("deletions found between repo revs")
893 893 for f in c:
894 894 getfile(f, mmap[f])
895 895 for f in r:
896 896 getfile(f, mmap[f])
897 util.set_exec(repo.wjoin(f), mmap.execf[f])
897 util.set_exec(repo.wjoin(f), mmap.execf(f))
898 898 repo.dirstate.update(c + r, 'n')
899 899 for f in a:
900 900 try: os.unlink(repo.wjoin(f))
901 901 except: raise
902 902 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
903 903 except: pass
904 904 if a:
905 905 repo.dirstate.forget(a)
906 906 repo.dirstate.setparents(qp, revlog.nullid)
907 907 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
908 908 del self.applied[start:end]
909 909 if len(self.applied):
910 910 self.ui.write("Now at: %s\n" % self.applied[-1].name)
911 911 else:
912 912 self.ui.write("Patch queue now empty\n")
913 913
914 914 def diff(self, repo, files):
915 915 top = self.check_toppatch(repo)
916 916 if not top:
917 917 self.ui.write("No patches applied\n")
918 918 return
919 919 qp = self.qparents(repo, top)
920 920 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
921 921
922 922 def refresh(self, repo, msg=None, short=False):
923 923 if len(self.applied) == 0:
924 924 self.ui.write("No patches applied\n")
925 925 return
926 926 wlock = repo.wlock()
927 927 self.check_toppatch(repo)
928 928 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
929 929 top = revlog.bin(top)
930 930 cparents = repo.changelog.parents(top)
931 931 patchparent = self.qparents(repo, top)
932 932 message, comments, user, date, patchfound = self.readheaders(patch)
933 933
934 934 patchf = self.opener(patch, "w")
935 935 msg = msg.rstrip()
936 936 if msg:
937 937 if comments:
938 938 # Remove existing message.
939 939 ci = 0
940 940 for mi in range(len(message)):
941 941 while message[mi] != comments[ci]:
942 942 ci += 1
943 943 del comments[ci]
944 944 comments.append(msg)
945 945 if comments:
946 946 comments = "\n".join(comments) + '\n\n'
947 947 patchf.write(comments)
948 948
949 949 tip = repo.changelog.tip()
950 950 if top == tip:
951 951 # if the top of our patch queue is also the tip, there is an
952 952 # optimization here. We update the dirstate in place and strip
953 953 # off the tip commit. Then just commit the current directory
954 954 # tree. We can also send repo.commit the list of files
955 955 # changed to speed up the diff
956 956 #
957 957 # in short mode, we only diff the files included in the
958 958 # patch already
959 959 #
960 960 # this should really read:
961 961 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
962 962 # but we do it backwards to take advantage of manifest/chlog
963 963 # caching against the next repo.changes call
964 964 #
965 965 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
966 966 if short:
967 967 filelist = cc + aa + dd
968 968 else:
969 969 filelist = None
970 970 (c, a, r, d, u) = repo.changes(None, None, filelist)
971 971
972 972 # we might end up with files that were added between tip and
973 973 # the dirstate parent, but then changed in the local dirstate.
974 974 # in this case, we want them to only show up in the added section
975 975 for x in c:
976 976 if x not in aa:
977 977 cc.append(x)
978 978 # we might end up with files added by the local dirstate that
979 979 # were deleted by the patch. In this case, they should only
980 980 # show up in the changed section.
981 981 for x in a:
982 982 if x in dd:
983 983 del dd[dd.index(x)]
984 984 cc.append(x)
985 985 else:
986 986 aa.append(x)
987 987 # make sure any files deleted in the local dirstate
988 988 # are not in the add or change column of the patch
989 989 forget = []
990 990 for x in d + r:
991 991 if x in aa:
992 992 del aa[aa.index(x)]
993 993 forget.append(x)
994 994 continue
995 995 elif x in cc:
996 996 del cc[cc.index(x)]
997 997 dd.append(x)
998 998
999 999 c = list(util.unique(cc))
1000 1000 r = list(util.unique(dd))
1001 1001 a = list(util.unique(aa))
1002 1002 filelist = list(util.unique(c + r + a ))
1003 1003 commands.dodiff(patchf, self.ui, repo, patchparent, None,
1004 1004 filelist, changes=(c, a, r, [], u))
1005 1005 patchf.close()
1006 1006
1007 1007 changes = repo.changelog.read(tip)
1008 1008 repo.dirstate.setparents(*cparents)
1009 1009 repo.dirstate.update(a, 'a')
1010 1010 repo.dirstate.update(r, 'r')
1011 1011 repo.dirstate.update(c, 'n')
1012 1012 repo.dirstate.forget(forget)
1013 1013
1014 1014 if not msg:
1015 1015 if not message:
1016 1016 message = "patch queue: %s\n" % patch
1017 1017 else:
1018 1018 message = "\n".join(message)
1019 1019 else:
1020 1020 message = msg
1021 1021
1022 1022 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1023 1023 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1024 1024 self.applied[-1] = statusentry(revlog.hex(n), patch)
1025 1025 self.applied_dirty = 1
1026 1026 else:
1027 1027 commands.dodiff(patchf, self.ui, repo, patchparent, None)
1028 1028 patchf.close()
1029 1029 self.pop(repo, force=True, wlock=wlock)
1030 1030 self.push(repo, force=True, wlock=wlock)
1031 1031
1032 1032 def init(self, repo, create=False):
1033 1033 if os.path.isdir(self.path):
1034 1034 raise util.Abort(_("patch queue directory already exists"))
1035 1035 os.mkdir(self.path)
1036 1036 if create:
1037 1037 return self.qrepo(create=True)
1038 1038
1039 1039 def unapplied(self, repo, patch=None):
1040 1040 if patch and patch not in self.series:
1041 1041 raise util.Abort(_("patch %s is not in series file") % patch)
1042 1042 if not patch:
1043 1043 start = self.series_end()
1044 1044 else:
1045 1045 start = self.series.index(patch) + 1
1046 1046 unapplied = []
1047 1047 for i in xrange(start, len(self.series)):
1048 1048 pushable, reason = self.pushable(i)
1049 1049 if pushable:
1050 1050 unapplied.append((i, self.series[i]))
1051 1051 self.explain_pushable(i)
1052 1052 return unapplied
1053 1053
1054 1054 def qseries(self, repo, missing=None, summary=False):
1055 1055 start = self.series_end(all_patches=True)
1056 1056 if not missing:
1057 1057 for i in range(len(self.series)):
1058 1058 patch = self.series[i]
1059 1059 if self.ui.verbose:
1060 1060 if i < start:
1061 1061 status = 'A'
1062 1062 elif self.pushable(i)[0]:
1063 1063 status = 'U'
1064 1064 else:
1065 1065 status = 'G'
1066 1066 self.ui.write('%d %s ' % (i, status))
1067 1067 if summary:
1068 1068 msg = self.readheaders(patch)[0]
1069 1069 msg = msg and ': ' + msg[0] or ': '
1070 1070 else:
1071 1071 msg = ''
1072 1072 self.ui.write('%s%s\n' % (patch, msg))
1073 1073 else:
1074 1074 msng_list = []
1075 1075 for root, dirs, files in os.walk(self.path):
1076 1076 d = root[len(self.path) + 1:]
1077 1077 for f in files:
1078 1078 fl = os.path.join(d, f)
1079 1079 if (fl not in self.series and
1080 1080 fl not in (self.status_path, self.series_path)
1081 1081 and not fl.startswith('.')):
1082 1082 msng_list.append(fl)
1083 1083 msng_list.sort()
1084 1084 for x in msng_list:
1085 1085 if self.ui.verbose:
1086 1086 self.ui.write("D ")
1087 1087 self.ui.write("%s\n" % x)
1088 1088
1089 1089 def issaveline(self, l):
1090 1090 if l.name == '.hg.patches.save.line':
1091 1091 return True
1092 1092
1093 1093 def qrepo(self, create=False):
1094 1094 if create or os.path.isdir(self.join(".hg")):
1095 1095 return hg.repository(self.ui, path=self.path, create=create)
1096 1096
1097 1097 def restore(self, repo, rev, delete=None, qupdate=None):
1098 1098 c = repo.changelog.read(rev)
1099 1099 desc = c[4].strip()
1100 1100 lines = desc.splitlines()
1101 1101 i = 0
1102 1102 datastart = None
1103 1103 series = []
1104 1104 applied = []
1105 1105 qpp = None
1106 1106 for i in xrange(0, len(lines)):
1107 1107 if lines[i] == 'Patch Data:':
1108 1108 datastart = i + 1
1109 1109 elif lines[i].startswith('Dirstate:'):
1110 1110 l = lines[i].rstrip()
1111 1111 l = l[10:].split(' ')
1112 1112 qpp = [ hg.bin(x) for x in l ]
1113 1113 elif datastart != None:
1114 1114 l = lines[i].rstrip()
1115 1115 se = statusentry(l)
1116 1116 file_ = se.name
1117 1117 if se.rev:
1118 1118 applied.append(se)
1119 1119 series.append(file_)
1120 1120 if datastart == None:
1121 1121 self.ui.warn("No saved patch data found\n")
1122 1122 return 1
1123 1123 self.ui.warn("restoring status: %s\n" % lines[0])
1124 1124 self.full_series = series
1125 1125 self.applied = applied
1126 1126 self.parse_series()
1127 1127 self.series_dirty = 1
1128 1128 self.applied_dirty = 1
1129 1129 heads = repo.changelog.heads()
1130 1130 if delete:
1131 1131 if rev not in heads:
1132 1132 self.ui.warn("save entry has children, leaving it alone\n")
1133 1133 else:
1134 1134 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1135 1135 pp = repo.dirstate.parents()
1136 1136 if rev in pp:
1137 1137 update = True
1138 1138 else:
1139 1139 update = False
1140 1140 self.strip(repo, rev, update=update, backup='strip')
1141 1141 if qpp:
1142 1142 self.ui.warn("saved queue repository parents: %s %s\n" %
1143 1143 (hg.short(qpp[0]), hg.short(qpp[1])))
1144 1144 if qupdate:
1145 1145 print "queue directory updating"
1146 1146 r = self.qrepo()
1147 1147 if not r:
1148 1148 self.ui.warn("Unable to load queue repository\n")
1149 1149 return 1
1150 1150 hg.clean(r, qpp[0])
1151 1151
1152 1152 def save(self, repo, msg=None):
1153 1153 if len(self.applied) == 0:
1154 1154 self.ui.warn("save: no patches applied, exiting\n")
1155 1155 return 1
1156 1156 if self.issaveline(self.applied[-1]):
1157 1157 self.ui.warn("status is already saved\n")
1158 1158 return 1
1159 1159
1160 1160 ar = [ ':' + x for x in self.full_series ]
1161 1161 if not msg:
1162 1162 msg = "hg patches saved state"
1163 1163 else:
1164 1164 msg = "hg patches: " + msg.rstrip('\r\n')
1165 1165 r = self.qrepo()
1166 1166 if r:
1167 1167 pp = r.dirstate.parents()
1168 1168 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1169 1169 msg += "\n\nPatch Data:\n"
1170 1170 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1171 1171 "\n".join(ar) + '\n' or "")
1172 1172 n = repo.commit(None, text, user=None, force=1)
1173 1173 if not n:
1174 1174 self.ui.warn("repo commit failed\n")
1175 1175 return 1
1176 1176 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1177 1177 self.applied_dirty = 1
1178 1178
1179 1179 def full_series_end(self):
1180 1180 if len(self.applied) > 0:
1181 1181 p = self.applied[-1].name
1182 1182 end = self.find_series(p)
1183 1183 if end == None:
1184 1184 return len(self.full_series)
1185 1185 return end + 1
1186 1186 return 0
1187 1187
1188 1188 def series_end(self, all_patches=False):
1189 1189 end = 0
1190 1190 def next(start):
1191 1191 if all_patches:
1192 1192 return start
1193 1193 i = start
1194 1194 while i < len(self.series):
1195 1195 p, reason = self.pushable(i)
1196 1196 if p:
1197 1197 break
1198 1198 self.explain_pushable(i)
1199 1199 i += 1
1200 1200 return i
1201 1201 if len(self.applied) > 0:
1202 1202 p = self.applied[-1].name
1203 1203 try:
1204 1204 end = self.series.index(p)
1205 1205 except ValueError:
1206 1206 return 0
1207 1207 return next(end + 1)
1208 1208 return next(end)
1209 1209
1210 1210 def qapplied(self, repo, patch=None):
1211 1211 if patch and patch not in self.series:
1212 1212 raise util.Abort(_("patch %s is not in series file") % patch)
1213 1213 if not patch:
1214 1214 end = len(self.applied)
1215 1215 else:
1216 1216 end = self.series.index(patch) + 1
1217 1217 for x in xrange(end):
1218 1218 p = self.appliedname(x)
1219 1219 self.ui.write("%s\n" % p)
1220 1220
1221 1221 def appliedname(self, index):
1222 1222 pname = self.applied[index].name
1223 1223 if not self.ui.verbose:
1224 1224 p = pname
1225 1225 else:
1226 1226 p = str(self.series.index(pname)) + " " + p
1227 1227 return p
1228 1228
1229 1229 def top(self, repo):
1230 1230 if len(self.applied):
1231 1231 p = self.appliedname(-1)
1232 1232 self.ui.write(p + '\n')
1233 1233 else:
1234 1234 self.ui.write("No patches applied\n")
1235 1235
1236 1236 def next(self, repo):
1237 1237 end = self.series_end()
1238 1238 if end == len(self.series):
1239 1239 self.ui.write("All patches applied\n")
1240 1240 else:
1241 1241 p = self.series[end]
1242 1242 if self.ui.verbose:
1243 1243 self.ui.write("%d " % self.series.index(p))
1244 1244 self.ui.write(p + '\n')
1245 1245
1246 1246 def prev(self, repo):
1247 1247 if len(self.applied) > 1:
1248 1248 p = self.appliedname(-2)
1249 1249 self.ui.write(p + '\n')
1250 1250 elif len(self.applied) == 1:
1251 1251 self.ui.write("Only one patch applied\n")
1252 1252 else:
1253 1253 self.ui.write("No patches applied\n")
1254 1254
1255 1255 def qimport(self, repo, files, patch=None, existing=None, force=None):
1256 1256 if len(files) > 1 and patch:
1257 1257 raise util.Abort(_('option "-n" not valid when importing multiple '
1258 1258 'files'))
1259 1259 i = 0
1260 1260 added = []
1261 1261 for filename in files:
1262 1262 if existing:
1263 1263 if not patch:
1264 1264 patch = filename
1265 1265 if not os.path.isfile(self.join(patch)):
1266 1266 raise util.Abort(_("patch %s does not exist") % patch)
1267 1267 else:
1268 1268 try:
1269 1269 text = file(filename).read()
1270 1270 except IOError:
1271 1271 raise util.Abort(_("unable to read %s") % patch)
1272 1272 if not patch:
1273 1273 patch = os.path.split(filename)[1]
1274 1274 if not force and os.path.exists(self.join(patch)):
1275 1275 raise util.Abort(_('patch "%s" already exists') % patch)
1276 1276 patchf = self.opener(patch, "w")
1277 1277 patchf.write(text)
1278 1278 if patch in self.series:
1279 1279 raise util.Abort(_('patch %s is already in the series file')
1280 1280 % patch)
1281 1281 index = self.full_series_end() + i
1282 1282 self.full_series[index:index] = [patch]
1283 1283 self.parse_series()
1284 1284 self.ui.warn("adding %s to series file\n" % patch)
1285 1285 i += 1
1286 1286 added.append(patch)
1287 1287 patch = None
1288 1288 self.series_dirty = 1
1289 1289 qrepo = self.qrepo()
1290 1290 if qrepo:
1291 1291 qrepo.add(added)
1292 1292
1293 1293 def delete(ui, repo, patch, **opts):
1294 1294 """remove a patch from the series file
1295 1295
1296 1296 The patch must not be applied.
1297 1297 With -f, deletes the patch file as well as the series entry."""
1298 1298 q = repo.mq
1299 1299 q.delete(repo, patch, force=opts.get('force'))
1300 1300 q.save_dirty()
1301 1301 return 0
1302 1302
1303 1303 def applied(ui, repo, patch=None, **opts):
1304 1304 """print the patches already applied"""
1305 1305 repo.mq.qapplied(repo, patch)
1306 1306 return 0
1307 1307
1308 1308 def unapplied(ui, repo, patch=None, **opts):
1309 1309 """print the patches not yet applied"""
1310 1310 for i, p in repo.mq.unapplied(repo, patch):
1311 1311 if ui.verbose:
1312 1312 ui.write("%d " % i)
1313 1313 ui.write("%s\n" % p)
1314 1314
1315 1315 def qimport(ui, repo, *filename, **opts):
1316 1316 """import a patch"""
1317 1317 q = repo.mq
1318 1318 q.qimport(repo, filename, patch=opts['name'],
1319 1319 existing=opts['existing'], force=opts['force'])
1320 1320 q.save_dirty()
1321 1321 return 0
1322 1322
1323 1323 def init(ui, repo, **opts):
1324 1324 """init a new queue repository
1325 1325
1326 1326 The queue repository is unversioned by default. If -c is
1327 1327 specified, qinit will create a separate nested repository
1328 1328 for patches. Use qcommit to commit changes to this queue
1329 1329 repository."""
1330 1330 q = repo.mq
1331 1331 r = q.init(repo, create=opts['create_repo'])
1332 1332 q.save_dirty()
1333 1333 if r:
1334 1334 fp = r.wopener('.hgignore', 'w')
1335 1335 print >> fp, 'syntax: glob'
1336 1336 print >> fp, 'status'
1337 1337 fp.close()
1338 1338 r.wopener('series', 'w').close()
1339 1339 r.add(['.hgignore', 'series'])
1340 1340 return 0
1341 1341
1342 1342 def clone(ui, source, dest=None, **opts):
1343 1343 '''clone main and patch repository at same time
1344 1344
1345 1345 If source is local, destination will have no patches applied. If
1346 1346 source is remote, this command can not check if patches are
1347 1347 applied in source, so cannot guarantee that patches are not
1348 1348 applied in destination. If you clone remote repository, be sure
1349 1349 before that it has no patches applied.
1350 1350
1351 1351 Source patch repository is looked for in <src>/.hg/patches by
1352 1352 default. Use -p <url> to change.
1353 1353 '''
1354 1354 commands.setremoteconfig(ui, opts)
1355 1355 if dest is None:
1356 1356 dest = hg.defaultdest(source)
1357 1357 sr = hg.repository(ui, ui.expandpath(source))
1358 1358 qbase, destrev = None, None
1359 1359 if sr.local():
1360 1360 reposetup(ui, sr)
1361 1361 if sr.mq.applied:
1362 1362 qbase = revlog.bin(sr.mq.applied[0].rev)
1363 1363 if not hg.islocal(dest):
1364 1364 destrev = sr.parents(qbase)[0]
1365 1365 ui.note(_('cloning main repo\n'))
1366 1366 sr, dr = hg.clone(ui, sr, dest,
1367 1367 pull=opts['pull'],
1368 1368 rev=destrev,
1369 1369 update=False,
1370 1370 stream=opts['uncompressed'])
1371 1371 ui.note(_('cloning patch repo\n'))
1372 1372 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1373 1373 dr.url() + '/.hg/patches',
1374 1374 pull=opts['pull'],
1375 1375 update=not opts['noupdate'],
1376 1376 stream=opts['uncompressed'])
1377 1377 if dr.local():
1378 1378 if qbase:
1379 1379 ui.note(_('stripping applied patches from destination repo\n'))
1380 1380 reposetup(ui, dr)
1381 1381 dr.mq.strip(dr, qbase, update=False, backup=None)
1382 1382 if not opts['noupdate']:
1383 1383 ui.note(_('updating destination repo\n'))
1384 1384 hg.update(dr, dr.changelog.tip())
1385 1385
1386 1386 def commit(ui, repo, *pats, **opts):
1387 1387 """commit changes in the queue repository"""
1388 1388 q = repo.mq
1389 1389 r = q.qrepo()
1390 1390 if not r: raise util.Abort('no queue repository')
1391 1391 commands.commit(r.ui, r, *pats, **opts)
1392 1392
1393 1393 def series(ui, repo, **opts):
1394 1394 """print the entire series file"""
1395 1395 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1396 1396 return 0
1397 1397
1398 1398 def top(ui, repo, **opts):
1399 1399 """print the name of the current patch"""
1400 1400 repo.mq.top(repo)
1401 1401 return 0
1402 1402
1403 1403 def next(ui, repo, **opts):
1404 1404 """print the name of the next patch"""
1405 1405 repo.mq.next(repo)
1406 1406 return 0
1407 1407
1408 1408 def prev(ui, repo, **opts):
1409 1409 """print the name of the previous patch"""
1410 1410 repo.mq.prev(repo)
1411 1411 return 0
1412 1412
1413 1413 def new(ui, repo, patch, **opts):
1414 1414 """create a new patch
1415 1415
1416 1416 qnew creates a new patch on top of the currently-applied patch
1417 1417 (if any). It will refuse to run if there are any outstanding
1418 1418 changes unless -f is specified, in which case the patch will
1419 1419 be initialised with them.
1420 1420
1421 1421 -m or -l set the patch header as well as the commit message.
1422 1422 If neither is specified, the patch header is empty and the
1423 1423 commit message is 'New patch: PATCH'"""
1424 1424 q = repo.mq
1425 1425 message = commands.logmessage(opts)
1426 1426 q.new(repo, patch, msg=message, force=opts['force'])
1427 1427 q.save_dirty()
1428 1428 return 0
1429 1429
1430 1430 def refresh(ui, repo, **opts):
1431 1431 """update the current patch"""
1432 1432 q = repo.mq
1433 1433 message = commands.logmessage(opts)
1434 1434 if opts['edit']:
1435 1435 if message:
1436 1436 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1437 1437 patch = q.applied[-1].name
1438 1438 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1439 1439 message = ui.edit('\n'.join(message), user or ui.username())
1440 1440 q.refresh(repo, msg=message, short=opts['short'])
1441 1441 q.save_dirty()
1442 1442 return 0
1443 1443
1444 1444 def diff(ui, repo, *files, **opts):
1445 1445 """diff of the current patch"""
1446 1446 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1447 1447 repo.mq.diff(repo, list(files))
1448 1448 return 0
1449 1449
1450 1450 def fold(ui, repo, *files, **opts):
1451 1451 """fold the named patches into the current patch
1452 1452
1453 1453 Patches must not yet be applied. Each patch will be successively
1454 1454 applied to the current patch in the order given. If all the
1455 1455 patches apply successfully, the current patch will be refreshed
1456 1456 with the new cumulative patch, and the folded patches will
1457 1457 be deleted. With -f/--force, the folded patch files will
1458 1458 be removed afterwards.
1459 1459
1460 1460 The header for each folded patch will be concatenated with
1461 1461 the current patch header, separated by a line of '* * *'."""
1462 1462
1463 1463 q = repo.mq
1464 1464
1465 1465 if not files:
1466 1466 raise util.Abort(_('qfold requires at least one patch name'))
1467 1467 if not q.check_toppatch(repo):
1468 1468 raise util.Abort(_('No patches applied\n'))
1469 1469
1470 1470 message = commands.logmessage(opts)
1471 1471 if opts['edit']:
1472 1472 if message:
1473 1473 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1474 1474
1475 1475 parent = q.lookup('qtip')
1476 1476 patches = []
1477 1477 messages = []
1478 1478 for f in files:
1479 1479 patch = q.lookup(f)
1480 1480 if patch in patches or patch == parent:
1481 1481 ui.warn(_('Skipping already folded patch %s') % patch)
1482 1482 if q.isapplied(patch):
1483 1483 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1484 1484 patches.append(patch)
1485 1485
1486 1486 for patch in patches:
1487 1487 if not message:
1488 1488 messages.append(q.readheaders(patch)[0])
1489 1489 pf = q.join(patch)
1490 1490 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1491 1491 if not patchsuccess:
1492 1492 raise util.Abort(_('Error folding patch %s') % patch)
1493 1493
1494 1494 if not message:
1495 1495 message, comments, user = q.readheaders(parent)[0:3]
1496 1496 for msg in messages:
1497 1497 message.append('* * *')
1498 1498 message.extend(msg)
1499 1499 message = '\n'.join(message)
1500 1500
1501 1501 if opts['edit']:
1502 1502 message = ui.edit(message, user or ui.username())
1503 1503
1504 1504 q.refresh(repo, msg=message)
1505 1505
1506 1506 for patch in patches:
1507 1507 q.delete(repo, patch, force=opts['force'])
1508 1508
1509 1509 q.save_dirty()
1510 1510
1511 1511 def guard(ui, repo, *args, **opts):
1512 1512 '''set or print guards for a patch
1513 1513
1514 1514 guards control whether a patch can be pushed. a patch with no
1515 1515 guards is aways pushed. a patch with posative guard ("+foo") is
1516 1516 pushed only if qselect command enables guard "foo". a patch with
1517 1517 nagative guard ("-foo") is never pushed if qselect command enables
1518 1518 guard "foo".
1519 1519
1520 1520 with no arguments, default is to print current active guards.
1521 1521 with arguments, set active guards for patch.
1522 1522
1523 1523 to set nagative guard "-foo" on topmost patch ("--" is needed so
1524 1524 hg will not interpret "-foo" as argument):
1525 1525 hg qguard -- -foo
1526 1526
1527 1527 to set guards on other patch:
1528 1528 hg qguard other.patch +2.6.17 -stable
1529 1529 '''
1530 1530 def status(idx):
1531 1531 guards = q.series_guards[idx] or ['unguarded']
1532 1532 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1533 1533 q = repo.mq
1534 1534 patch = None
1535 1535 args = list(args)
1536 1536 if opts['list']:
1537 1537 if args or opts['none']:
1538 1538 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1539 1539 for i in xrange(len(q.series)):
1540 1540 status(i)
1541 1541 return
1542 1542 if not args or args[0][0:1] in '-+':
1543 1543 if not q.applied:
1544 1544 raise util.Abort(_('no patches applied'))
1545 1545 patch = q.applied[-1].name
1546 1546 if patch is None and args[0][0:1] not in '-+':
1547 1547 patch = args.pop(0)
1548 1548 if patch is None:
1549 1549 raise util.Abort(_('no patch to work with'))
1550 1550 if args or opts['none']:
1551 1551 q.set_guards(q.find_series(patch), args)
1552 1552 q.save_dirty()
1553 1553 else:
1554 1554 status(q.series.index(q.lookup(patch)))
1555 1555
1556 1556 def header(ui, repo, patch=None):
1557 1557 """Print the header of the topmost or specified patch"""
1558 1558 q = repo.mq
1559 1559
1560 1560 if patch:
1561 1561 patch = q.lookup(patch)
1562 1562 else:
1563 1563 if not q.applied:
1564 1564 ui.write('No patches applied\n')
1565 1565 return
1566 1566 patch = q.lookup('qtip')
1567 1567 message = repo.mq.readheaders(patch)[0]
1568 1568
1569 1569 ui.write('\n'.join(message) + '\n')
1570 1570
1571 1571 def lastsavename(path):
1572 1572 (directory, base) = os.path.split(path)
1573 1573 names = os.listdir(directory)
1574 1574 namere = re.compile("%s.([0-9]+)" % base)
1575 1575 maxindex = None
1576 1576 maxname = None
1577 1577 for f in names:
1578 1578 m = namere.match(f)
1579 1579 if m:
1580 1580 index = int(m.group(1))
1581 1581 if maxindex == None or index > maxindex:
1582 1582 maxindex = index
1583 1583 maxname = f
1584 1584 if maxname:
1585 1585 return (os.path.join(directory, maxname), maxindex)
1586 1586 return (None, None)
1587 1587
1588 1588 def savename(path):
1589 1589 (last, index) = lastsavename(path)
1590 1590 if last is None:
1591 1591 index = 0
1592 1592 newpath = path + ".%d" % (index + 1)
1593 1593 return newpath
1594 1594
1595 1595 def push(ui, repo, patch=None, **opts):
1596 1596 """push the next patch onto the stack"""
1597 1597 q = repo.mq
1598 1598 mergeq = None
1599 1599
1600 1600 if opts['all']:
1601 1601 patch = q.series[-1]
1602 1602 if opts['merge']:
1603 1603 if opts['name']:
1604 1604 newpath = opts['name']
1605 1605 else:
1606 1606 newpath, i = lastsavename(q.path)
1607 1607 if not newpath:
1608 1608 ui.warn("no saved queues found, please use -n\n")
1609 1609 return 1
1610 1610 mergeq = queue(ui, repo.join(""), newpath)
1611 1611 ui.warn("merging with queue at: %s\n" % mergeq.path)
1612 1612 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1613 1613 mergeq=mergeq)
1614 1614 q.save_dirty()
1615 1615 return ret
1616 1616
1617 1617 def pop(ui, repo, patch=None, **opts):
1618 1618 """pop the current patch off the stack"""
1619 1619 localupdate = True
1620 1620 if opts['name']:
1621 1621 q = queue(ui, repo.join(""), repo.join(opts['name']))
1622 1622 ui.warn('using patch queue: %s\n' % q.path)
1623 1623 localupdate = False
1624 1624 else:
1625 1625 q = repo.mq
1626 1626 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1627 1627 q.save_dirty()
1628 1628 return 0
1629 1629
1630 1630 def rename(ui, repo, patch, name=None, **opts):
1631 1631 """rename a patch
1632 1632
1633 1633 With one argument, renames the current patch to PATCH1.
1634 1634 With two arguments, renames PATCH1 to PATCH2."""
1635 1635
1636 1636 q = repo.mq
1637 1637
1638 1638 if not name:
1639 1639 name = patch
1640 1640 patch = None
1641 1641
1642 1642 if name in q.series:
1643 1643 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1644 1644
1645 1645 absdest = q.join(name)
1646 1646 if os.path.exists(absdest):
1647 1647 raise util.Abort(_('%s already exists') % absdest)
1648 1648
1649 1649 if patch:
1650 1650 patch = q.lookup(patch)
1651 1651 else:
1652 1652 if not q.applied:
1653 1653 ui.write(_('No patches applied\n'))
1654 1654 return
1655 1655 patch = q.lookup('qtip')
1656 1656
1657 1657 if ui.verbose:
1658 1658 ui.write('Renaming %s to %s\n' % (patch, name))
1659 1659 i = q.find_series(patch)
1660 1660 q.full_series[i] = name
1661 1661 q.parse_series()
1662 1662 q.series_dirty = 1
1663 1663
1664 1664 info = q.isapplied(patch)
1665 1665 if info:
1666 1666 q.applied[info[0]] = statusentry(info[1], name)
1667 1667 q.applied_dirty = 1
1668 1668
1669 1669 util.rename(q.join(patch), absdest)
1670 1670 r = q.qrepo()
1671 1671 if r:
1672 1672 wlock = r.wlock()
1673 1673 if r.dirstate.state(name) == 'r':
1674 1674 r.undelete([name], wlock)
1675 1675 r.copy(patch, name, wlock)
1676 1676 r.remove([patch], False, wlock)
1677 1677
1678 1678 q.save_dirty()
1679 1679
1680 1680 def restore(ui, repo, rev, **opts):
1681 1681 """restore the queue state saved by a rev"""
1682 1682 rev = repo.lookup(rev)
1683 1683 q = repo.mq
1684 1684 q.restore(repo, rev, delete=opts['delete'],
1685 1685 qupdate=opts['update'])
1686 1686 q.save_dirty()
1687 1687 return 0
1688 1688
1689 1689 def save(ui, repo, **opts):
1690 1690 """save current queue state"""
1691 1691 q = repo.mq
1692 1692 message = commands.logmessage(opts)
1693 1693 ret = q.save(repo, msg=message)
1694 1694 if ret:
1695 1695 return ret
1696 1696 q.save_dirty()
1697 1697 if opts['copy']:
1698 1698 path = q.path
1699 1699 if opts['name']:
1700 1700 newpath = os.path.join(q.basepath, opts['name'])
1701 1701 if os.path.exists(newpath):
1702 1702 if not os.path.isdir(newpath):
1703 1703 raise util.Abort(_('destination %s exists and is not '
1704 1704 'a directory') % newpath)
1705 1705 if not opts['force']:
1706 1706 raise util.Abort(_('destination %s exists, '
1707 1707 'use -f to force') % newpath)
1708 1708 else:
1709 1709 newpath = savename(path)
1710 1710 ui.warn("copy %s to %s\n" % (path, newpath))
1711 1711 util.copyfiles(path, newpath)
1712 1712 if opts['empty']:
1713 1713 try:
1714 1714 os.unlink(q.join(q.status_path))
1715 1715 except:
1716 1716 pass
1717 1717 return 0
1718 1718
1719 1719 def strip(ui, repo, rev, **opts):
1720 1720 """strip a revision and all later revs on the same branch"""
1721 1721 rev = repo.lookup(rev)
1722 1722 backup = 'all'
1723 1723 if opts['backup']:
1724 1724 backup = 'strip'
1725 1725 elif opts['nobackup']:
1726 1726 backup = 'none'
1727 1727 repo.mq.strip(repo, rev, backup=backup)
1728 1728 return 0
1729 1729
1730 1730 def select(ui, repo, *args, **opts):
1731 1731 '''set or print guarded patches to push
1732 1732
1733 1733 use qguard command to set or print guards on patch. then use
1734 1734 qselect to tell mq which guards to use. example:
1735 1735
1736 1736 qguard foo.patch -stable (nagative guard)
1737 1737 qguard bar.patch +stable (posative guard)
1738 1738 qselect stable
1739 1739
1740 1740 this sets "stable" guard. mq will skip foo.patch (because it has
1741 1741 nagative match) but push bar.patch (because it has posative
1742 1742 match). patch is pushed only if all posative guards match and no
1743 1743 nagative guards match.
1744 1744
1745 1745 with no arguments, default is to print current active guards.
1746 1746 with arguments, set active guards as given.
1747 1747
1748 1748 use -n/--none to deactivate guards (no other arguments needed).
1749 1749 when no guards active, patches with posative guards are skipped,
1750 1750 patches with nagative guards are pushed.
1751 1751
1752 1752 use -s/--series to print list of all guards in series file (no
1753 1753 other arguments needed). use -v for more information.'''
1754 1754
1755 1755 q = repo.mq
1756 1756 guards = q.active()
1757 1757 if args or opts['none']:
1758 1758 q.set_active(args)
1759 1759 q.save_dirty()
1760 1760 if not args:
1761 1761 ui.status(_('guards deactivated\n'))
1762 1762 if q.series:
1763 1763 ui.status(_('%d of %d unapplied patches active\n') %
1764 1764 (len(q.unapplied(repo)), len(q.series)))
1765 1765 elif opts['series']:
1766 1766 guards = {}
1767 1767 noguards = 0
1768 1768 for gs in q.series_guards:
1769 1769 if not gs:
1770 1770 noguards += 1
1771 1771 for g in gs:
1772 1772 guards.setdefault(g, 0)
1773 1773 guards[g] += 1
1774 1774 if ui.verbose:
1775 1775 guards['NONE'] = noguards
1776 1776 guards = guards.items()
1777 1777 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1778 1778 if guards:
1779 1779 ui.note(_('guards in series file:\n'))
1780 1780 for guard, count in guards:
1781 1781 ui.note('%2d ' % count)
1782 1782 ui.write(guard, '\n')
1783 1783 else:
1784 1784 ui.note(_('no guards in series file\n'))
1785 1785 else:
1786 1786 if guards:
1787 1787 ui.note(_('active guards:\n'))
1788 1788 for g in guards:
1789 1789 ui.write(g, '\n')
1790 1790 else:
1791 1791 ui.write(_('no active guards\n'))
1792 1792
1793 1793 def reposetup(ui, repo):
1794 1794 class mqrepo(repo.__class__):
1795 1795 def tags(self):
1796 1796 if self.tagscache:
1797 1797 return self.tagscache
1798 1798
1799 1799 tagscache = super(mqrepo, self).tags()
1800 1800
1801 1801 q = self.mq
1802 1802 if not q.applied:
1803 1803 return tagscache
1804 1804
1805 1805 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1806 1806 mqtags.append((mqtags[-1][0], 'qtip'))
1807 1807 mqtags.append((mqtags[0][0], 'qbase'))
1808 1808 for patch in mqtags:
1809 1809 if patch[1] in tagscache:
1810 1810 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1811 1811 else:
1812 1812 tagscache[patch[1]] = revlog.bin(patch[0])
1813 1813
1814 1814 return tagscache
1815 1815
1816 1816 repo.__class__ = mqrepo
1817 1817 repo.mq = queue(ui, repo.join(""))
1818 1818
1819 1819 cmdtable = {
1820 1820 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1821 1821 "qclone": (clone,
1822 1822 [('', 'pull', None, _('use pull protocol to copy metadata')),
1823 1823 ('U', 'noupdate', None, _('do not update the new working directories')),
1824 1824 ('', 'uncompressed', None,
1825 1825 _('use uncompressed transfer (fast over LAN)')),
1826 1826 ('e', 'ssh', '', _('specify ssh command to use')),
1827 1827 ('p', 'patches', '', _('location of source patch repo')),
1828 1828 ('', 'remotecmd', '',
1829 1829 _('specify hg command to run on the remote side'))],
1830 1830 'hg qclone [OPTION]... SOURCE [DEST]'),
1831 1831 "qcommit|qci":
1832 1832 (commit,
1833 1833 commands.table["^commit|ci"][1],
1834 1834 'hg qcommit [OPTION]... [FILE]...'),
1835 1835 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1836 1836 "qdelete":
1837 1837 (delete,
1838 1838 [('f', 'force', None, _('delete patch file'))],
1839 1839 'hg qdelete [-f] PATCH'),
1840 1840 'qfold':
1841 1841 (fold,
1842 1842 [('e', 'edit', None, _('edit patch header')),
1843 1843 ('f', 'force', None, _('delete folded patch files')),
1844 1844 ('m', 'message', '', _('set patch header to <text>')),
1845 1845 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1846 1846 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1847 1847 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1848 1848 ('n', 'none', None, _('drop all guards'))],
1849 1849 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1850 1850 'qheader': (header, [],
1851 1851 _('hg qheader [PATCH]')),
1852 1852 "^qimport":
1853 1853 (qimport,
1854 1854 [('e', 'existing', None, 'import file in patch dir'),
1855 1855 ('n', 'name', '', 'patch file name'),
1856 1856 ('f', 'force', None, 'overwrite existing files')],
1857 1857 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1858 1858 "^qinit":
1859 1859 (init,
1860 1860 [('c', 'create-repo', None, 'create queue repository')],
1861 1861 'hg qinit [-c]'),
1862 1862 "qnew":
1863 1863 (new,
1864 1864 [('m', 'message', '', _('use <text> as commit message')),
1865 1865 ('l', 'logfile', '', _('read the commit message from <file>')),
1866 1866 ('f', 'force', None, _('import uncommitted changes into patch'))],
1867 1867 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1868 1868 "qnext": (next, [], 'hg qnext'),
1869 1869 "qprev": (prev, [], 'hg qprev'),
1870 1870 "^qpop":
1871 1871 (pop,
1872 1872 [('a', 'all', None, 'pop all patches'),
1873 1873 ('n', 'name', '', 'queue name to pop'),
1874 1874 ('f', 'force', None, 'forget any local changes')],
1875 1875 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1876 1876 "^qpush":
1877 1877 (push,
1878 1878 [('f', 'force', None, 'apply if the patch has rejects'),
1879 1879 ('l', 'list', None, 'list patch name in commit text'),
1880 1880 ('a', 'all', None, 'apply all patches'),
1881 1881 ('m', 'merge', None, 'merge from another queue'),
1882 1882 ('n', 'name', '', 'merge queue name')],
1883 1883 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1884 1884 "^qrefresh":
1885 1885 (refresh,
1886 1886 [('e', 'edit', None, _('edit commit message')),
1887 1887 ('m', 'message', '', _('change commit message with <text>')),
1888 1888 ('l', 'logfile', '', _('change commit message with <file> content')),
1889 1889 ('s', 'short', None, 'short refresh')],
1890 1890 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'),
1891 1891 'qrename|qmv':
1892 1892 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1893 1893 "qrestore":
1894 1894 (restore,
1895 1895 [('d', 'delete', None, 'delete save entry'),
1896 1896 ('u', 'update', None, 'update queue working dir')],
1897 1897 'hg qrestore [-d] [-u] REV'),
1898 1898 "qsave":
1899 1899 (save,
1900 1900 [('m', 'message', '', _('use <text> as commit message')),
1901 1901 ('l', 'logfile', '', _('read the commit message from <file>')),
1902 1902 ('c', 'copy', None, 'copy patch directory'),
1903 1903 ('n', 'name', '', 'copy directory name'),
1904 1904 ('e', 'empty', None, 'clear queue status file'),
1905 1905 ('f', 'force', None, 'force copy')],
1906 1906 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1907 1907 "qselect": (select,
1908 1908 [('n', 'none', None, _('disable all guards')),
1909 1909 ('s', 'series', None, _('list all guards in series file'))],
1910 1910 'hg qselect [GUARDS]'),
1911 1911 "qseries":
1912 1912 (series,
1913 1913 [('m', 'missing', None, 'print patches not in series'),
1914 1914 ('s', 'summary', None, _('print first line of patch header'))],
1915 1915 'hg qseries [-m]'),
1916 1916 "^strip":
1917 1917 (strip,
1918 1918 [('f', 'force', None, 'force multi-head removal'),
1919 1919 ('b', 'backup', None, 'bundle unrelated changesets'),
1920 1920 ('n', 'nobackup', None, 'no backups')],
1921 1921 'hg strip [-f] [-b] [-n] REV'),
1922 1922 "qtop": (top, [], 'hg qtop'),
1923 1923 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1924 1924 }
1925 1925
General Comments 0
You need to be logged in to leave comments. Login now