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