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