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