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