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