##// END OF EJS Templates
qfinish: document --applied instead of --all
Martin Geisler -
r6937:3aa8ad0e default
parent child Browse files
Show More
@@ -1,2457 +1,2458 b''
1 1 # mq.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.i18n import _
33 33 from mercurial.node import bin, hex, short
34 34 from mercurial.repo import RepoError
35 35 from mercurial import commands, cmdutil, hg, patch, revlog, util
36 36 from mercurial import repair
37 37 import os, sys, re, errno
38 38
39 39 commands.norepo += " qclone"
40 40
41 41 # Patch names looks like unix-file names.
42 42 # They must be joinable with queue directory and result in the patch path.
43 43 normname = util.normpath
44 44
45 45 class statusentry:
46 46 def __init__(self, rev, name=None):
47 47 if not name:
48 48 fields = rev.split(':', 1)
49 49 if len(fields) == 2:
50 50 self.rev, self.name = fields
51 51 else:
52 52 self.rev, self.name = None, None
53 53 else:
54 54 self.rev, self.name = rev, name
55 55
56 56 def __str__(self):
57 57 return self.rev + ':' + self.name
58 58
59 59 class queue:
60 60 def __init__(self, ui, path, patchdir=None):
61 61 self.basepath = path
62 62 self.path = patchdir or os.path.join(path, "patches")
63 63 self.opener = util.opener(self.path)
64 64 self.ui = ui
65 65 self.applied = []
66 66 self.full_series = []
67 67 self.applied_dirty = 0
68 68 self.series_dirty = 0
69 69 self.series_path = "series"
70 70 self.status_path = "status"
71 71 self.guards_path = "guards"
72 72 self.active_guards = None
73 73 self.guards_dirty = False
74 74 self._diffopts = None
75 75
76 76 if os.path.exists(self.join(self.series_path)):
77 77 self.full_series = self.opener(self.series_path).read().splitlines()
78 78 self.parse_series()
79 79
80 80 if os.path.exists(self.join(self.status_path)):
81 81 lines = self.opener(self.status_path).read().splitlines()
82 82 self.applied = [statusentry(l) for l in lines]
83 83
84 84 def diffopts(self):
85 85 if self._diffopts is None:
86 86 self._diffopts = patch.diffopts(self.ui)
87 87 return self._diffopts
88 88
89 89 def join(self, *p):
90 90 return os.path.join(self.path, *p)
91 91
92 92 def find_series(self, patch):
93 93 pre = re.compile("(\s*)([^#]+)")
94 94 index = 0
95 95 for l in self.full_series:
96 96 m = pre.match(l)
97 97 if m:
98 98 s = m.group(2)
99 99 s = s.rstrip()
100 100 if s == patch:
101 101 return index
102 102 index += 1
103 103 return None
104 104
105 105 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
106 106
107 107 def parse_series(self):
108 108 self.series = []
109 109 self.series_guards = []
110 110 for l in self.full_series:
111 111 h = l.find('#')
112 112 if h == -1:
113 113 patch = l
114 114 comment = ''
115 115 elif h == 0:
116 116 continue
117 117 else:
118 118 patch = l[:h]
119 119 comment = l[h:]
120 120 patch = patch.strip()
121 121 if patch:
122 122 if patch in self.series:
123 123 raise util.Abort(_('%s appears more than once in %s') %
124 124 (patch, self.join(self.series_path)))
125 125 self.series.append(patch)
126 126 self.series_guards.append(self.guard_re.findall(comment))
127 127
128 128 def check_guard(self, guard):
129 129 if not guard:
130 130 return _('guard cannot be an empty string')
131 131 bad_chars = '# \t\r\n\f'
132 132 first = guard[0]
133 133 for c in '-+':
134 134 if first == c:
135 135 return (_('guard %r starts with invalid character: %r') %
136 136 (guard, c))
137 137 for c in bad_chars:
138 138 if c in guard:
139 139 return _('invalid character in guard %r: %r') % (guard, c)
140 140
141 141 def set_active(self, guards):
142 142 for guard in guards:
143 143 bad = self.check_guard(guard)
144 144 if bad:
145 145 raise util.Abort(bad)
146 146 guards = util.sort(util.unique(guards))
147 147 self.ui.debug('active guards: %s\n' % ' '.join(guards))
148 148 self.active_guards = guards
149 149 self.guards_dirty = True
150 150
151 151 def active(self):
152 152 if self.active_guards is None:
153 153 self.active_guards = []
154 154 try:
155 155 guards = self.opener(self.guards_path).read().split()
156 156 except IOError, err:
157 157 if err.errno != errno.ENOENT: raise
158 158 guards = []
159 159 for i, guard in enumerate(guards):
160 160 bad = self.check_guard(guard)
161 161 if bad:
162 162 self.ui.warn('%s:%d: %s\n' %
163 163 (self.join(self.guards_path), i + 1, bad))
164 164 else:
165 165 self.active_guards.append(guard)
166 166 return self.active_guards
167 167
168 168 def set_guards(self, idx, guards):
169 169 for g in guards:
170 170 if len(g) < 2:
171 171 raise util.Abort(_('guard %r too short') % g)
172 172 if g[0] not in '-+':
173 173 raise util.Abort(_('guard %r starts with invalid char') % g)
174 174 bad = self.check_guard(g[1:])
175 175 if bad:
176 176 raise util.Abort(bad)
177 177 drop = self.guard_re.sub('', self.full_series[idx])
178 178 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
179 179 self.parse_series()
180 180 self.series_dirty = True
181 181
182 182 def pushable(self, idx):
183 183 if isinstance(idx, str):
184 184 idx = self.series.index(idx)
185 185 patchguards = self.series_guards[idx]
186 186 if not patchguards:
187 187 return True, None
188 188 default = False
189 189 guards = self.active()
190 190 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
191 191 if exactneg:
192 192 return False, exactneg[0]
193 193 pos = [g for g in patchguards if g[0] == '+']
194 194 exactpos = [g for g in pos if g[1:] in guards]
195 195 if pos:
196 196 if exactpos:
197 197 return True, exactpos[0]
198 198 return False, pos
199 199 return True, ''
200 200
201 201 def explain_pushable(self, idx, all_patches=False):
202 202 write = all_patches and self.ui.write or self.ui.warn
203 203 if all_patches or self.ui.verbose:
204 204 if isinstance(idx, str):
205 205 idx = self.series.index(idx)
206 206 pushable, why = self.pushable(idx)
207 207 if all_patches and pushable:
208 208 if why is None:
209 209 write(_('allowing %s - no guards in effect\n') %
210 210 self.series[idx])
211 211 else:
212 212 if not why:
213 213 write(_('allowing %s - no matching negative guards\n') %
214 214 self.series[idx])
215 215 else:
216 216 write(_('allowing %s - guarded by %r\n') %
217 217 (self.series[idx], why))
218 218 if not pushable:
219 219 if why:
220 220 write(_('skipping %s - guarded by %r\n') %
221 221 (self.series[idx], why))
222 222 else:
223 223 write(_('skipping %s - no matching guards\n') %
224 224 self.series[idx])
225 225
226 226 def save_dirty(self):
227 227 def write_list(items, path):
228 228 fp = self.opener(path, 'w')
229 229 for i in items:
230 230 fp.write("%s\n" % i)
231 231 fp.close()
232 232 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
233 233 if self.series_dirty: write_list(self.full_series, self.series_path)
234 234 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
235 235
236 236 def readheaders(self, patch):
237 237 def eatdiff(lines):
238 238 while lines:
239 239 l = lines[-1]
240 240 if (l.startswith("diff -") or
241 241 l.startswith("Index:") or
242 242 l.startswith("===========")):
243 243 del lines[-1]
244 244 else:
245 245 break
246 246 def eatempty(lines):
247 247 while lines:
248 248 l = lines[-1]
249 249 if re.match('\s*$', l):
250 250 del lines[-1]
251 251 else:
252 252 break
253 253
254 254 pf = self.join(patch)
255 255 message = []
256 256 comments = []
257 257 user = None
258 258 date = None
259 259 format = None
260 260 subject = None
261 261 diffstart = 0
262 262
263 263 for line in file(pf):
264 264 line = line.rstrip()
265 265 if line.startswith('diff --git'):
266 266 diffstart = 2
267 267 break
268 268 if diffstart:
269 269 if line.startswith('+++ '):
270 270 diffstart = 2
271 271 break
272 272 if line.startswith("--- "):
273 273 diffstart = 1
274 274 continue
275 275 elif format == "hgpatch":
276 276 # parse values when importing the result of an hg export
277 277 if line.startswith("# User "):
278 278 user = line[7:]
279 279 elif line.startswith("# Date "):
280 280 date = line[7:]
281 281 elif not line.startswith("# ") and line:
282 282 message.append(line)
283 283 format = None
284 284 elif line == '# HG changeset patch':
285 285 format = "hgpatch"
286 286 elif (format != "tagdone" and (line.startswith("Subject: ") or
287 287 line.startswith("subject: "))):
288 288 subject = line[9:]
289 289 format = "tag"
290 290 elif (format != "tagdone" and (line.startswith("From: ") or
291 291 line.startswith("from: "))):
292 292 user = line[6:]
293 293 format = "tag"
294 294 elif format == "tag" and line == "":
295 295 # when looking for tags (subject: from: etc) they
296 296 # end once you find a blank line in the source
297 297 format = "tagdone"
298 298 elif message or line:
299 299 message.append(line)
300 300 comments.append(line)
301 301
302 302 eatdiff(message)
303 303 eatdiff(comments)
304 304 eatempty(message)
305 305 eatempty(comments)
306 306
307 307 # make sure message isn't empty
308 308 if format and format.startswith("tag") and subject:
309 309 message.insert(0, "")
310 310 message.insert(0, subject)
311 311 return (message, comments, user, date, diffstart > 1)
312 312
313 313 def removeundo(self, repo):
314 314 undo = repo.sjoin('undo')
315 315 if not os.path.exists(undo):
316 316 return
317 317 try:
318 318 os.unlink(undo)
319 319 except OSError, inst:
320 320 self.ui.warn('error removing undo: %s\n' % str(inst))
321 321
322 322 def printdiff(self, repo, node1, node2=None, files=None,
323 323 fp=None, changes=None, opts={}):
324 324 m = cmdutil.match(repo, files, opts)
325 325 patch.diff(repo, node1, node2, m, fp, changes, self.diffopts())
326 326
327 327 def mergeone(self, repo, mergeq, head, patch, rev):
328 328 # first try just applying the patch
329 329 (err, n) = self.apply(repo, [ patch ], update_status=False,
330 330 strict=True, merge=rev)
331 331
332 332 if err == 0:
333 333 return (err, n)
334 334
335 335 if n is None:
336 336 raise util.Abort(_("apply failed for patch %s") % patch)
337 337
338 338 self.ui.warn("patch didn't work out, merging %s\n" % patch)
339 339
340 340 # apply failed, strip away that rev and merge.
341 341 hg.clean(repo, head)
342 342 self.strip(repo, n, update=False, backup='strip')
343 343
344 344 ctx = repo[rev]
345 345 ret = hg.merge(repo, rev)
346 346 if ret:
347 347 raise util.Abort(_("update returned %d") % ret)
348 348 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
349 349 if n == None:
350 350 raise util.Abort(_("repo commit failed"))
351 351 try:
352 352 message, comments, user, date, patchfound = mergeq.readheaders(patch)
353 353 except:
354 354 raise util.Abort(_("unable to read %s") % patch)
355 355
356 356 patchf = self.opener(patch, "w")
357 357 if comments:
358 358 comments = "\n".join(comments) + '\n\n'
359 359 patchf.write(comments)
360 360 self.printdiff(repo, head, n, fp=patchf)
361 361 patchf.close()
362 362 self.removeundo(repo)
363 363 return (0, n)
364 364
365 365 def qparents(self, repo, rev=None):
366 366 if rev is None:
367 367 (p1, p2) = repo.dirstate.parents()
368 368 if p2 == revlog.nullid:
369 369 return p1
370 370 if len(self.applied) == 0:
371 371 return None
372 372 return revlog.bin(self.applied[-1].rev)
373 373 pp = repo.changelog.parents(rev)
374 374 if pp[1] != revlog.nullid:
375 375 arevs = [ x.rev for x in self.applied ]
376 376 p0 = revlog.hex(pp[0])
377 377 p1 = revlog.hex(pp[1])
378 378 if p0 in arevs:
379 379 return pp[0]
380 380 if p1 in arevs:
381 381 return pp[1]
382 382 return pp[0]
383 383
384 384 def mergepatch(self, repo, mergeq, series):
385 385 if len(self.applied) == 0:
386 386 # each of the patches merged in will have two parents. This
387 387 # can confuse the qrefresh, qdiff, and strip code because it
388 388 # needs to know which parent is actually in the patch queue.
389 389 # so, we insert a merge marker with only one parent. This way
390 390 # the first patch in the queue is never a merge patch
391 391 #
392 392 pname = ".hg.patches.merge.marker"
393 393 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
394 394 self.removeundo(repo)
395 395 self.applied.append(statusentry(revlog.hex(n), pname))
396 396 self.applied_dirty = 1
397 397
398 398 head = self.qparents(repo)
399 399
400 400 for patch in series:
401 401 patch = mergeq.lookup(patch, strict=True)
402 402 if not patch:
403 403 self.ui.warn("patch %s does not exist\n" % patch)
404 404 return (1, None)
405 405 pushable, reason = self.pushable(patch)
406 406 if not pushable:
407 407 self.explain_pushable(patch, all_patches=True)
408 408 continue
409 409 info = mergeq.isapplied(patch)
410 410 if not info:
411 411 self.ui.warn("patch %s is not applied\n" % patch)
412 412 return (1, None)
413 413 rev = revlog.bin(info[1])
414 414 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
415 415 if head:
416 416 self.applied.append(statusentry(revlog.hex(head), patch))
417 417 self.applied_dirty = 1
418 418 if err:
419 419 return (err, head)
420 420 self.save_dirty()
421 421 return (0, head)
422 422
423 423 def patch(self, repo, patchfile):
424 424 '''Apply patchfile to the working directory.
425 425 patchfile: file name of patch'''
426 426 files = {}
427 427 try:
428 428 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
429 429 files=files)
430 430 except Exception, inst:
431 431 self.ui.note(str(inst) + '\n')
432 432 if not self.ui.verbose:
433 433 self.ui.warn("patch failed, unable to continue (try -v)\n")
434 434 return (False, files, False)
435 435
436 436 return (True, files, fuzz)
437 437
438 438 def apply(self, repo, series, list=False, update_status=True,
439 439 strict=False, patchdir=None, merge=None, all_files={}):
440 440 wlock = lock = tr = None
441 441 try:
442 442 wlock = repo.wlock()
443 443 lock = repo.lock()
444 444 tr = repo.transaction()
445 445 try:
446 446 ret = self._apply(repo, series, list, update_status,
447 447 strict, patchdir, merge, all_files=all_files)
448 448 tr.close()
449 449 self.save_dirty()
450 450 return ret
451 451 except:
452 452 try:
453 453 tr.abort()
454 454 finally:
455 455 repo.invalidate()
456 456 repo.dirstate.invalidate()
457 457 raise
458 458 finally:
459 459 del tr, lock, wlock
460 460 self.removeundo(repo)
461 461
462 462 def _apply(self, repo, series, list=False, update_status=True,
463 463 strict=False, patchdir=None, merge=None, all_files={}):
464 464 # TODO unify with commands.py
465 465 if not patchdir:
466 466 patchdir = self.path
467 467 err = 0
468 468 n = None
469 469 for patchname in series:
470 470 pushable, reason = self.pushable(patchname)
471 471 if not pushable:
472 472 self.explain_pushable(patchname, all_patches=True)
473 473 continue
474 474 self.ui.warn("applying %s\n" % patchname)
475 475 pf = os.path.join(patchdir, patchname)
476 476
477 477 try:
478 478 message, comments, user, date, patchfound = self.readheaders(patchname)
479 479 except:
480 480 self.ui.warn("Unable to read %s\n" % patchname)
481 481 err = 1
482 482 break
483 483
484 484 if not message:
485 485 message = "imported patch %s\n" % patchname
486 486 else:
487 487 if list:
488 488 message.append("\nimported patch %s" % patchname)
489 489 message = '\n'.join(message)
490 490
491 491 (patcherr, files, fuzz) = self.patch(repo, pf)
492 492 all_files.update(files)
493 493 patcherr = not patcherr
494 494
495 495 if merge and files:
496 496 # Mark as removed/merged and update dirstate parent info
497 497 removed = []
498 498 merged = []
499 499 for f in files:
500 500 if os.path.exists(repo.wjoin(f)):
501 501 merged.append(f)
502 502 else:
503 503 removed.append(f)
504 504 for f in removed:
505 505 repo.dirstate.remove(f)
506 506 for f in merged:
507 507 repo.dirstate.merge(f)
508 508 p1, p2 = repo.dirstate.parents()
509 509 repo.dirstate.setparents(p1, merge)
510 510
511 511 files = patch.updatedir(self.ui, repo, files)
512 512 match = cmdutil.matchfiles(repo, files or [])
513 513 n = repo.commit(files, message, user, date, match=match,
514 514 force=True)
515 515
516 516 if n == None:
517 517 raise util.Abort(_("repo commit failed"))
518 518
519 519 if update_status:
520 520 self.applied.append(statusentry(revlog.hex(n), patchname))
521 521
522 522 if patcherr:
523 523 if not patchfound:
524 524 self.ui.warn("patch %s is empty\n" % patchname)
525 525 err = 0
526 526 else:
527 527 self.ui.warn("patch failed, rejects left in working dir\n")
528 528 err = 1
529 529 break
530 530
531 531 if fuzz and strict:
532 532 self.ui.warn("fuzz found when applying patch, stopping\n")
533 533 err = 1
534 534 break
535 535 return (err, n)
536 536
537 537 def _clean_series(self, patches):
538 538 indices = util.sort([self.find_series(p) for p in patches])
539 539 for i in indices[-1::-1]:
540 540 del self.full_series[i]
541 541 self.parse_series()
542 542 self.series_dirty = 1
543 543
544 544 def finish(self, repo, revs):
545 545 revs.sort()
546 546 firstrev = repo[self.applied[0].rev].rev()
547 547 appliedbase = 0
548 548 patches = []
549 549 for rev in util.sort(revs):
550 550 if rev < firstrev:
551 551 raise util.Abort(_('revision %d is not managed') % rev)
552 552 base = revlog.bin(self.applied[appliedbase].rev)
553 553 node = repo.changelog.node(rev)
554 554 if node != base:
555 555 raise util.Abort(_('cannot delete revision %d above '
556 556 'applied patches') % rev)
557 557 patches.append(self.applied[appliedbase].name)
558 558 appliedbase += 1
559 559
560 560 r = self.qrepo()
561 561 if r:
562 562 r.remove(patches, True)
563 563 else:
564 564 for p in patches:
565 565 os.unlink(self.join(p))
566 566
567 567 del self.applied[:appliedbase]
568 568 self.applied_dirty = 1
569 569 self._clean_series(patches)
570 570
571 571 def delete(self, repo, patches, opts):
572 572 if not patches and not opts.get('rev'):
573 573 raise util.Abort(_('qdelete requires at least one revision or '
574 574 'patch name'))
575 575
576 576 realpatches = []
577 577 for patch in patches:
578 578 patch = self.lookup(patch, strict=True)
579 579 info = self.isapplied(patch)
580 580 if info:
581 581 raise util.Abort(_("cannot delete applied patch %s") % patch)
582 582 if patch not in self.series:
583 583 raise util.Abort(_("patch %s not in series file") % patch)
584 584 realpatches.append(patch)
585 585
586 586 appliedbase = 0
587 587 if opts.get('rev'):
588 588 if not self.applied:
589 589 raise util.Abort(_('no patches applied'))
590 590 revs = cmdutil.revrange(repo, opts['rev'])
591 591 if len(revs) > 1 and revs[0] > revs[1]:
592 592 revs.reverse()
593 593 for rev in revs:
594 594 if appliedbase >= len(self.applied):
595 595 raise util.Abort(_("revision %d is not managed") % rev)
596 596
597 597 base = revlog.bin(self.applied[appliedbase].rev)
598 598 node = repo.changelog.node(rev)
599 599 if node != base:
600 600 raise util.Abort(_("cannot delete revision %d above "
601 601 "applied patches") % rev)
602 602 realpatches.append(self.applied[appliedbase].name)
603 603 appliedbase += 1
604 604
605 605 if not opts.get('keep'):
606 606 r = self.qrepo()
607 607 if r:
608 608 r.remove(realpatches, True)
609 609 else:
610 610 for p in realpatches:
611 611 os.unlink(self.join(p))
612 612
613 613 if appliedbase:
614 614 del self.applied[:appliedbase]
615 615 self.applied_dirty = 1
616 616 self._clean_series(realpatches)
617 617
618 618 def check_toppatch(self, repo):
619 619 if len(self.applied) > 0:
620 620 top = revlog.bin(self.applied[-1].rev)
621 621 pp = repo.dirstate.parents()
622 622 if top not in pp:
623 623 raise util.Abort(_("working directory revision is not qtip"))
624 624 return top
625 625 return None
626 626 def check_localchanges(self, repo, force=False, refresh=True):
627 627 m, a, r, d = repo.status()[:4]
628 628 if m or a or r or d:
629 629 if not force:
630 630 if refresh:
631 631 raise util.Abort(_("local changes found, refresh first"))
632 632 else:
633 633 raise util.Abort(_("local changes found"))
634 634 return m, a, r, d
635 635
636 636 _reserved = ('series', 'status', 'guards')
637 637 def check_reserved_name(self, name):
638 638 if (name in self._reserved or name.startswith('.hg')
639 639 or name.startswith('.mq')):
640 640 raise util.Abort(_('"%s" cannot be used as the name of a patch')
641 641 % name)
642 642
643 643 def new(self, repo, patch, *pats, **opts):
644 644 msg = opts.get('msg')
645 645 force = opts.get('force')
646 646 user = opts.get('user')
647 647 date = opts.get('date')
648 648 if date:
649 649 date = util.parsedate(date)
650 650 self.check_reserved_name(patch)
651 651 if os.path.exists(self.join(patch)):
652 652 raise util.Abort(_('patch "%s" already exists') % patch)
653 653 if opts.get('include') or opts.get('exclude') or pats:
654 654 match = cmdutil.match(repo, pats, opts)
655 655 m, a, r, d = repo.status(match=match)[:4]
656 656 else:
657 657 m, a, r, d = self.check_localchanges(repo, force)
658 658 match = cmdutil.match(repo, m + a + r)
659 659 commitfiles = m + a + r
660 660 self.check_toppatch(repo)
661 661 wlock = repo.wlock()
662 662 try:
663 663 insert = self.full_series_end()
664 664 commitmsg = msg and msg or ("[mq]: %s" % patch)
665 665 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
666 666 if n == None:
667 667 raise util.Abort(_("repo commit failed"))
668 668 self.full_series[insert:insert] = [patch]
669 669 self.applied.append(statusentry(revlog.hex(n), patch))
670 670 self.parse_series()
671 671 self.series_dirty = 1
672 672 self.applied_dirty = 1
673 673 p = self.opener(patch, "w")
674 674 if date:
675 675 p.write("# HG changeset patch\n")
676 676 if user:
677 677 p.write("# User " + user + "\n")
678 678 p.write("# Date %d %d\n" % date)
679 679 p.write("\n")
680 680 elif user:
681 681 p.write("From: " + user + "\n")
682 682 p.write("\n")
683 683 if msg:
684 684 msg = msg + "\n"
685 685 p.write(msg)
686 686 p.close()
687 687 wlock = None
688 688 r = self.qrepo()
689 689 if r: r.add([patch])
690 690 if commitfiles:
691 691 self.refresh(repo, short=True, git=opts.get('git'))
692 692 self.removeundo(repo)
693 693 finally:
694 694 del wlock
695 695
696 696 def strip(self, repo, rev, update=True, backup="all", force=None):
697 697 wlock = lock = None
698 698 try:
699 699 wlock = repo.wlock()
700 700 lock = repo.lock()
701 701
702 702 if update:
703 703 self.check_localchanges(repo, force=force, refresh=False)
704 704 urev = self.qparents(repo, rev)
705 705 hg.clean(repo, urev)
706 706 repo.dirstate.write()
707 707
708 708 self.removeundo(repo)
709 709 repair.strip(self.ui, repo, rev, backup)
710 710 # strip may have unbundled a set of backed up revisions after
711 711 # the actual strip
712 712 self.removeundo(repo)
713 713 finally:
714 714 del lock, wlock
715 715
716 716 def isapplied(self, patch):
717 717 """returns (index, rev, patch)"""
718 718 for i in xrange(len(self.applied)):
719 719 a = self.applied[i]
720 720 if a.name == patch:
721 721 return (i, a.rev, a.name)
722 722 return None
723 723
724 724 # if the exact patch name does not exist, we try a few
725 725 # variations. If strict is passed, we try only #1
726 726 #
727 727 # 1) a number to indicate an offset in the series file
728 728 # 2) a unique substring of the patch name was given
729 729 # 3) patchname[-+]num to indicate an offset in the series file
730 730 def lookup(self, patch, strict=False):
731 731 patch = patch and str(patch)
732 732
733 733 def partial_name(s):
734 734 if s in self.series:
735 735 return s
736 736 matches = [x for x in self.series if s in x]
737 737 if len(matches) > 1:
738 738 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
739 739 for m in matches:
740 740 self.ui.warn(' %s\n' % m)
741 741 return None
742 742 if matches:
743 743 return matches[0]
744 744 if len(self.series) > 0 and len(self.applied) > 0:
745 745 if s == 'qtip':
746 746 return self.series[self.series_end(True)-1]
747 747 if s == 'qbase':
748 748 return self.series[0]
749 749 return None
750 750 if patch == None:
751 751 return None
752 752
753 753 # we don't want to return a partial match until we make
754 754 # sure the file name passed in does not exist (checked below)
755 755 res = partial_name(patch)
756 756 if res and res == patch:
757 757 return res
758 758
759 759 if not os.path.isfile(self.join(patch)):
760 760 try:
761 761 sno = int(patch)
762 762 except(ValueError, OverflowError):
763 763 pass
764 764 else:
765 765 if sno < len(self.series):
766 766 return self.series[sno]
767 767 if not strict:
768 768 # return any partial match made above
769 769 if res:
770 770 return res
771 771 minus = patch.rfind('-')
772 772 if minus >= 0:
773 773 res = partial_name(patch[:minus])
774 774 if res:
775 775 i = self.series.index(res)
776 776 try:
777 777 off = int(patch[minus+1:] or 1)
778 778 except(ValueError, OverflowError):
779 779 pass
780 780 else:
781 781 if i - off >= 0:
782 782 return self.series[i - off]
783 783 plus = patch.rfind('+')
784 784 if plus >= 0:
785 785 res = partial_name(patch[:plus])
786 786 if res:
787 787 i = self.series.index(res)
788 788 try:
789 789 off = int(patch[plus+1:] or 1)
790 790 except(ValueError, OverflowError):
791 791 pass
792 792 else:
793 793 if i + off < len(self.series):
794 794 return self.series[i + off]
795 795 raise util.Abort(_("patch %s not in series") % patch)
796 796
797 797 def push(self, repo, patch=None, force=False, list=False,
798 798 mergeq=None):
799 799 wlock = repo.wlock()
800 800 if repo.dirstate.parents()[0] != repo.changelog.tip():
801 801 self.ui.status(_("(working directory not at tip)\n"))
802 802
803 803 try:
804 804 patch = self.lookup(patch)
805 805 # Suppose our series file is: A B C and the current 'top'
806 806 # patch is B. qpush C should be performed (moving forward)
807 807 # qpush B is a NOP (no change) qpush A is an error (can't
808 808 # go backwards with qpush)
809 809 if patch:
810 810 info = self.isapplied(patch)
811 811 if info:
812 812 if info[0] < len(self.applied) - 1:
813 813 raise util.Abort(
814 814 _("cannot push to a previous patch: %s") % patch)
815 815 if info[0] < len(self.series) - 1:
816 816 self.ui.warn(
817 817 _('qpush: %s is already at the top\n') % patch)
818 818 else:
819 819 self.ui.warn(_('all patches are currently applied\n'))
820 820 return
821 821
822 822 # Following the above example, starting at 'top' of B:
823 823 # qpush should be performed (pushes C), but a subsequent
824 824 # qpush without an argument is an error (nothing to
825 825 # apply). This allows a loop of "...while hg qpush..." to
826 826 # work as it detects an error when done
827 827 if self.series_end() == len(self.series):
828 828 self.ui.warn(_('patch series already fully applied\n'))
829 829 return 1
830 830 if not force:
831 831 self.check_localchanges(repo)
832 832
833 833 self.applied_dirty = 1;
834 834 start = self.series_end()
835 835 if start > 0:
836 836 self.check_toppatch(repo)
837 837 if not patch:
838 838 patch = self.series[start]
839 839 end = start + 1
840 840 else:
841 841 end = self.series.index(patch, start) + 1
842 842 s = self.series[start:end]
843 843 all_files = {}
844 844 try:
845 845 if mergeq:
846 846 ret = self.mergepatch(repo, mergeq, s)
847 847 else:
848 848 ret = self.apply(repo, s, list, all_files=all_files)
849 849 except:
850 850 self.ui.warn(_('cleaning up working directory...'))
851 851 node = repo.dirstate.parents()[0]
852 852 hg.revert(repo, node, None)
853 853 unknown = repo.status(unknown=True)[4]
854 854 # only remove unknown files that we know we touched or
855 855 # created while patching
856 856 for f in unknown:
857 857 if f in all_files:
858 858 util.unlink(repo.wjoin(f))
859 859 self.ui.warn(_('done\n'))
860 860 raise
861 861 top = self.applied[-1].name
862 862 if ret[0]:
863 863 self.ui.write(
864 864 "Errors during apply, please fix and refresh %s\n" % top)
865 865 else:
866 866 self.ui.write("Now at: %s\n" % top)
867 867 return ret[0]
868 868 finally:
869 869 del wlock
870 870
871 871 def pop(self, repo, patch=None, force=False, update=True, all=False):
872 872 def getfile(f, rev, flags):
873 873 t = repo.file(f).read(rev)
874 874 repo.wwrite(f, t, flags)
875 875
876 876 wlock = repo.wlock()
877 877 try:
878 878 if patch:
879 879 # index, rev, patch
880 880 info = self.isapplied(patch)
881 881 if not info:
882 882 patch = self.lookup(patch)
883 883 info = self.isapplied(patch)
884 884 if not info:
885 885 raise util.Abort(_("patch %s is not applied") % patch)
886 886
887 887 if len(self.applied) == 0:
888 888 # Allow qpop -a to work repeatedly,
889 889 # but not qpop without an argument
890 890 self.ui.warn(_("no patches applied\n"))
891 891 return not all
892 892
893 893 if not update:
894 894 parents = repo.dirstate.parents()
895 895 rr = [ revlog.bin(x.rev) for x in self.applied ]
896 896 for p in parents:
897 897 if p in rr:
898 898 self.ui.warn("qpop: forcing dirstate update\n")
899 899 update = True
900 900
901 901 if not force and update:
902 902 self.check_localchanges(repo)
903 903
904 904 self.applied_dirty = 1;
905 905 end = len(self.applied)
906 906 if not patch:
907 907 if all:
908 908 popi = 0
909 909 else:
910 910 popi = len(self.applied) - 1
911 911 else:
912 912 popi = info[0] + 1
913 913 if popi >= end:
914 914 self.ui.warn("qpop: %s is already at the top\n" % patch)
915 915 return
916 916 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
917 917
918 918 start = info[0]
919 919 rev = revlog.bin(info[1])
920 920
921 921 if update:
922 922 top = self.check_toppatch(repo)
923 923
924 924 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
925 925 raise util.Abort("popping would remove a revision not "
926 926 "managed by this patch queue")
927 927
928 928 # we know there are no local changes, so we can make a simplified
929 929 # form of hg.update.
930 930 if update:
931 931 qp = self.qparents(repo, rev)
932 932 changes = repo.changelog.read(qp)
933 933 mmap = repo.manifest.read(changes[0])
934 934 m, a, r, d = repo.status(qp, top)[:4]
935 935 if d:
936 936 raise util.Abort("deletions found between repo revs")
937 937 for f in m:
938 938 getfile(f, mmap[f], mmap.flags(f))
939 939 for f in r:
940 940 getfile(f, mmap[f], mmap.flags(f))
941 941 for f in m + r:
942 942 repo.dirstate.normal(f)
943 943 for f in a:
944 944 try:
945 945 os.unlink(repo.wjoin(f))
946 946 except OSError, e:
947 947 if e.errno != errno.ENOENT:
948 948 raise
949 949 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
950 950 except: pass
951 951 repo.dirstate.forget(f)
952 952 repo.dirstate.setparents(qp, revlog.nullid)
953 953 del self.applied[start:end]
954 954 self.strip(repo, rev, update=False, backup='strip')
955 955 if len(self.applied):
956 956 self.ui.write("Now at: %s\n" % self.applied[-1].name)
957 957 else:
958 958 self.ui.write("Patch queue now empty\n")
959 959 finally:
960 960 del wlock
961 961
962 962 def diff(self, repo, pats, opts):
963 963 top = self.check_toppatch(repo)
964 964 if not top:
965 965 self.ui.write("No patches applied\n")
966 966 return
967 967 qp = self.qparents(repo, top)
968 968 self._diffopts = patch.diffopts(self.ui, opts)
969 969 self.printdiff(repo, qp, files=pats, opts=opts)
970 970
971 971 def refresh(self, repo, pats=None, **opts):
972 972 if len(self.applied) == 0:
973 973 self.ui.write("No patches applied\n")
974 974 return 1
975 975 newdate = opts.get('date')
976 976 if newdate:
977 977 newdate = '%d %d' % util.parsedate(newdate)
978 978 wlock = repo.wlock()
979 979 try:
980 980 self.check_toppatch(repo)
981 981 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
982 982 top = revlog.bin(top)
983 983 if repo.changelog.heads(top) != [top]:
984 984 raise util.Abort("cannot refresh a revision with children")
985 985 cparents = repo.changelog.parents(top)
986 986 patchparent = self.qparents(repo, top)
987 987 message, comments, user, date, patchfound = self.readheaders(patchfn)
988 988
989 989 patchf = self.opener(patchfn, 'r+')
990 990
991 991 # if the patch was a git patch, refresh it as a git patch
992 992 for line in patchf:
993 993 if line.startswith('diff --git'):
994 994 self.diffopts().git = True
995 995 break
996 996
997 997 msg = opts.get('msg', '').rstrip()
998 998 if msg and comments:
999 999 # Remove existing message, keeping the rest of the comments
1000 1000 # fields.
1001 1001 # If comments contains 'subject: ', message will prepend
1002 1002 # the field and a blank line.
1003 1003 if message:
1004 1004 subj = 'subject: ' + message[0].lower()
1005 1005 for i in xrange(len(comments)):
1006 1006 if subj == comments[i].lower():
1007 1007 del comments[i]
1008 1008 message = message[2:]
1009 1009 break
1010 1010 ci = 0
1011 1011 for mi in xrange(len(message)):
1012 1012 while message[mi] != comments[ci]:
1013 1013 ci += 1
1014 1014 del comments[ci]
1015 1015
1016 1016 def setheaderfield(comments, prefixes, new):
1017 1017 # Update all references to a field in the patch header.
1018 1018 # If none found, add it email style.
1019 1019 res = False
1020 1020 for prefix in prefixes:
1021 1021 for i in xrange(len(comments)):
1022 1022 if comments[i].startswith(prefix):
1023 1023 comments[i] = prefix + new
1024 1024 res = True
1025 1025 break
1026 1026 return res
1027 1027
1028 1028 newuser = opts.get('user')
1029 1029 if newuser:
1030 1030 if not setheaderfield(comments, ['From: ', '# User '], newuser):
1031 1031 try:
1032 1032 patchheaderat = comments.index('# HG changeset patch')
1033 1033 comments.insert(patchheaderat + 1,'# User ' + newuser)
1034 1034 except ValueError:
1035 1035 comments = ['From: ' + newuser, ''] + comments
1036 1036 user = newuser
1037 1037
1038 1038 if newdate:
1039 1039 if setheaderfield(comments, ['# Date '], newdate):
1040 1040 date = newdate
1041 1041
1042 1042 if msg:
1043 1043 comments.append(msg)
1044 1044
1045 1045 patchf.seek(0)
1046 1046 patchf.truncate()
1047 1047
1048 1048 if comments:
1049 1049 comments = "\n".join(comments) + '\n\n'
1050 1050 patchf.write(comments)
1051 1051
1052 1052 if opts.get('git'):
1053 1053 self.diffopts().git = True
1054 1054 matchfn = cmdutil.match(repo, pats, opts)
1055 1055 tip = repo.changelog.tip()
1056 1056 if top == tip:
1057 1057 # if the top of our patch queue is also the tip, there is an
1058 1058 # optimization here. We update the dirstate in place and strip
1059 1059 # off the tip commit. Then just commit the current directory
1060 1060 # tree. We can also send repo.commit the list of files
1061 1061 # changed to speed up the diff
1062 1062 #
1063 1063 # in short mode, we only diff the files included in the
1064 1064 # patch already
1065 1065 #
1066 1066 # this should really read:
1067 1067 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1068 1068 # but we do it backwards to take advantage of manifest/chlog
1069 1069 # caching against the next repo.status call
1070 1070 #
1071 1071 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1072 1072 changes = repo.changelog.read(tip)
1073 1073 man = repo.manifest.read(changes[0])
1074 1074 aaa = aa[:]
1075 1075 if opts.get('short'):
1076 1076 match = cmdutil.matchfiles(repo, mm + aa + dd)
1077 1077 else:
1078 1078 match = cmdutil.matchall(repo)
1079 1079 m, a, r, d = repo.status(match=match)[:4]
1080 1080
1081 1081 # we might end up with files that were added between
1082 1082 # tip and the dirstate parent, but then changed in the
1083 1083 # local dirstate. in this case, we want them to only
1084 1084 # show up in the added section
1085 1085 for x in m:
1086 1086 if x not in aa:
1087 1087 mm.append(x)
1088 1088 # we might end up with files added by the local dirstate that
1089 1089 # were deleted by the patch. In this case, they should only
1090 1090 # show up in the changed section.
1091 1091 for x in a:
1092 1092 if x in dd:
1093 1093 del dd[dd.index(x)]
1094 1094 mm.append(x)
1095 1095 else:
1096 1096 aa.append(x)
1097 1097 # make sure any files deleted in the local dirstate
1098 1098 # are not in the add or change column of the patch
1099 1099 forget = []
1100 1100 for x in d + r:
1101 1101 if x in aa:
1102 1102 del aa[aa.index(x)]
1103 1103 forget.append(x)
1104 1104 continue
1105 1105 elif x in mm:
1106 1106 del mm[mm.index(x)]
1107 1107 dd.append(x)
1108 1108
1109 1109 m = util.unique(mm)
1110 1110 r = util.unique(dd)
1111 1111 a = util.unique(aa)
1112 1112 c = [filter(matchfn, l) for l in (m, a, r)]
1113 1113 match = cmdutil.matchfiles(repo, util.unique(c[0] + c[1] + c[2]))
1114 1114 patch.diff(repo, patchparent, match=match,
1115 1115 fp=patchf, changes=c, opts=self.diffopts())
1116 1116 patchf.close()
1117 1117
1118 1118 repo.dirstate.setparents(*cparents)
1119 1119 copies = {}
1120 1120 for dst in a:
1121 1121 src = repo.dirstate.copied(dst)
1122 1122 if src is not None:
1123 1123 copies.setdefault(src, []).append(dst)
1124 1124 repo.dirstate.add(dst)
1125 1125 # remember the copies between patchparent and tip
1126 1126 # this may be slow, so don't do it if we're not tracking copies
1127 1127 if self.diffopts().git:
1128 1128 for dst in aaa:
1129 1129 f = repo.file(dst)
1130 1130 src = f.renamed(man[dst])
1131 1131 if src:
1132 1132 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1133 1133 if dst in a:
1134 1134 copies[src[0]].append(dst)
1135 1135 # we can't copy a file created by the patch itself
1136 1136 if dst in copies:
1137 1137 del copies[dst]
1138 1138 for src, dsts in copies.iteritems():
1139 1139 for dst in dsts:
1140 1140 repo.dirstate.copy(src, dst)
1141 1141 for f in r:
1142 1142 repo.dirstate.remove(f)
1143 1143 # if the patch excludes a modified file, mark that
1144 1144 # file with mtime=0 so status can see it.
1145 1145 mm = []
1146 1146 for i in xrange(len(m)-1, -1, -1):
1147 1147 if not matchfn(m[i]):
1148 1148 mm.append(m[i])
1149 1149 del m[i]
1150 1150 for f in m:
1151 1151 repo.dirstate.normal(f)
1152 1152 for f in mm:
1153 1153 repo.dirstate.normallookup(f)
1154 1154 for f in forget:
1155 1155 repo.dirstate.forget(f)
1156 1156
1157 1157 if not msg:
1158 1158 if not message:
1159 1159 message = "[mq]: %s\n" % patchfn
1160 1160 else:
1161 1161 message = "\n".join(message)
1162 1162 else:
1163 1163 message = msg
1164 1164
1165 1165 if not user:
1166 1166 user = changes[1]
1167 1167
1168 1168 self.applied.pop()
1169 1169 self.applied_dirty = 1
1170 1170 self.strip(repo, top, update=False,
1171 1171 backup='strip')
1172 1172 n = repo.commit(match.files(), message, user, date, match=match,
1173 1173 force=1)
1174 1174 self.applied.append(statusentry(revlog.hex(n), patchfn))
1175 1175 self.removeundo(repo)
1176 1176 else:
1177 1177 self.printdiff(repo, patchparent, fp=patchf)
1178 1178 patchf.close()
1179 1179 added = repo.status()[1]
1180 1180 for a in added:
1181 1181 f = repo.wjoin(a)
1182 1182 try:
1183 1183 os.unlink(f)
1184 1184 except OSError, e:
1185 1185 if e.errno != errno.ENOENT:
1186 1186 raise
1187 1187 try: os.removedirs(os.path.dirname(f))
1188 1188 except: pass
1189 1189 # forget the file copies in the dirstate
1190 1190 # push should readd the files later on
1191 1191 repo.dirstate.forget(a)
1192 1192 self.pop(repo, force=True)
1193 1193 self.push(repo, force=True)
1194 1194 finally:
1195 1195 del wlock
1196 1196
1197 1197 def init(self, repo, create=False):
1198 1198 if not create and os.path.isdir(self.path):
1199 1199 raise util.Abort(_("patch queue directory already exists"))
1200 1200 try:
1201 1201 os.mkdir(self.path)
1202 1202 except OSError, inst:
1203 1203 if inst.errno != errno.EEXIST or not create:
1204 1204 raise
1205 1205 if create:
1206 1206 return self.qrepo(create=True)
1207 1207
1208 1208 def unapplied(self, repo, patch=None):
1209 1209 if patch and patch not in self.series:
1210 1210 raise util.Abort(_("patch %s is not in series file") % patch)
1211 1211 if not patch:
1212 1212 start = self.series_end()
1213 1213 else:
1214 1214 start = self.series.index(patch) + 1
1215 1215 unapplied = []
1216 1216 for i in xrange(start, len(self.series)):
1217 1217 pushable, reason = self.pushable(i)
1218 1218 if pushable:
1219 1219 unapplied.append((i, self.series[i]))
1220 1220 self.explain_pushable(i)
1221 1221 return unapplied
1222 1222
1223 1223 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1224 1224 summary=False):
1225 1225 def displayname(patchname):
1226 1226 if summary:
1227 1227 msg = self.readheaders(patchname)[0]
1228 1228 msg = msg and ': ' + msg[0] or ': '
1229 1229 else:
1230 1230 msg = ''
1231 1231 return '%s%s' % (patchname, msg)
1232 1232
1233 1233 applied = dict.fromkeys([p.name for p in self.applied])
1234 1234 if length is None:
1235 1235 length = len(self.series) - start
1236 1236 if not missing:
1237 1237 for i in xrange(start, start+length):
1238 1238 patch = self.series[i]
1239 1239 if patch in applied:
1240 1240 stat = 'A'
1241 1241 elif self.pushable(i)[0]:
1242 1242 stat = 'U'
1243 1243 else:
1244 1244 stat = 'G'
1245 1245 pfx = ''
1246 1246 if self.ui.verbose:
1247 1247 pfx = '%d %s ' % (i, stat)
1248 1248 elif status and status != stat:
1249 1249 continue
1250 1250 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1251 1251 else:
1252 1252 msng_list = []
1253 1253 for root, dirs, files in os.walk(self.path):
1254 1254 d = root[len(self.path) + 1:]
1255 1255 for f in files:
1256 1256 fl = os.path.join(d, f)
1257 1257 if (fl not in self.series and
1258 1258 fl not in (self.status_path, self.series_path,
1259 1259 self.guards_path)
1260 1260 and not fl.startswith('.')):
1261 1261 msng_list.append(fl)
1262 1262 for x in util.sort(msng_list):
1263 1263 pfx = self.ui.verbose and ('D ') or ''
1264 1264 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1265 1265
1266 1266 def issaveline(self, l):
1267 1267 if l.name == '.hg.patches.save.line':
1268 1268 return True
1269 1269
1270 1270 def qrepo(self, create=False):
1271 1271 if create or os.path.isdir(self.join(".hg")):
1272 1272 return hg.repository(self.ui, path=self.path, create=create)
1273 1273
1274 1274 def restore(self, repo, rev, delete=None, qupdate=None):
1275 1275 c = repo.changelog.read(rev)
1276 1276 desc = c[4].strip()
1277 1277 lines = desc.splitlines()
1278 1278 i = 0
1279 1279 datastart = None
1280 1280 series = []
1281 1281 applied = []
1282 1282 qpp = None
1283 1283 for i in xrange(0, len(lines)):
1284 1284 if lines[i] == 'Patch Data:':
1285 1285 datastart = i + 1
1286 1286 elif lines[i].startswith('Dirstate:'):
1287 1287 l = lines[i].rstrip()
1288 1288 l = l[10:].split(' ')
1289 1289 qpp = [ bin(x) for x in l ]
1290 1290 elif datastart != None:
1291 1291 l = lines[i].rstrip()
1292 1292 se = statusentry(l)
1293 1293 file_ = se.name
1294 1294 if se.rev:
1295 1295 applied.append(se)
1296 1296 else:
1297 1297 series.append(file_)
1298 1298 if datastart == None:
1299 1299 self.ui.warn("No saved patch data found\n")
1300 1300 return 1
1301 1301 self.ui.warn("restoring status: %s\n" % lines[0])
1302 1302 self.full_series = series
1303 1303 self.applied = applied
1304 1304 self.parse_series()
1305 1305 self.series_dirty = 1
1306 1306 self.applied_dirty = 1
1307 1307 heads = repo.changelog.heads()
1308 1308 if delete:
1309 1309 if rev not in heads:
1310 1310 self.ui.warn("save entry has children, leaving it alone\n")
1311 1311 else:
1312 1312 self.ui.warn("removing save entry %s\n" % short(rev))
1313 1313 pp = repo.dirstate.parents()
1314 1314 if rev in pp:
1315 1315 update = True
1316 1316 else:
1317 1317 update = False
1318 1318 self.strip(repo, rev, update=update, backup='strip')
1319 1319 if qpp:
1320 1320 self.ui.warn("saved queue repository parents: %s %s\n" %
1321 1321 (short(qpp[0]), short(qpp[1])))
1322 1322 if qupdate:
1323 1323 self.ui.status(_("queue directory updating\n"))
1324 1324 r = self.qrepo()
1325 1325 if not r:
1326 1326 self.ui.warn("Unable to load queue repository\n")
1327 1327 return 1
1328 1328 hg.clean(r, qpp[0])
1329 1329
1330 1330 def save(self, repo, msg=None):
1331 1331 if len(self.applied) == 0:
1332 1332 self.ui.warn("save: no patches applied, exiting\n")
1333 1333 return 1
1334 1334 if self.issaveline(self.applied[-1]):
1335 1335 self.ui.warn("status is already saved\n")
1336 1336 return 1
1337 1337
1338 1338 ar = [ ':' + x for x in self.full_series ]
1339 1339 if not msg:
1340 1340 msg = "hg patches saved state"
1341 1341 else:
1342 1342 msg = "hg patches: " + msg.rstrip('\r\n')
1343 1343 r = self.qrepo()
1344 1344 if r:
1345 1345 pp = r.dirstate.parents()
1346 1346 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1347 1347 msg += "\n\nPatch Data:\n"
1348 1348 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1349 1349 "\n".join(ar) + '\n' or "")
1350 1350 n = repo.commit(None, text, user=None, force=1)
1351 1351 if not n:
1352 1352 self.ui.warn("repo commit failed\n")
1353 1353 return 1
1354 1354 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1355 1355 self.applied_dirty = 1
1356 1356 self.removeundo(repo)
1357 1357
1358 1358 def full_series_end(self):
1359 1359 if len(self.applied) > 0:
1360 1360 p = self.applied[-1].name
1361 1361 end = self.find_series(p)
1362 1362 if end == None:
1363 1363 return len(self.full_series)
1364 1364 return end + 1
1365 1365 return 0
1366 1366
1367 1367 def series_end(self, all_patches=False):
1368 1368 """If all_patches is False, return the index of the next pushable patch
1369 1369 in the series, or the series length. If all_patches is True, return the
1370 1370 index of the first patch past the last applied one.
1371 1371 """
1372 1372 end = 0
1373 1373 def next(start):
1374 1374 if all_patches:
1375 1375 return start
1376 1376 i = start
1377 1377 while i < len(self.series):
1378 1378 p, reason = self.pushable(i)
1379 1379 if p:
1380 1380 break
1381 1381 self.explain_pushable(i)
1382 1382 i += 1
1383 1383 return i
1384 1384 if len(self.applied) > 0:
1385 1385 p = self.applied[-1].name
1386 1386 try:
1387 1387 end = self.series.index(p)
1388 1388 except ValueError:
1389 1389 return 0
1390 1390 return next(end + 1)
1391 1391 return next(end)
1392 1392
1393 1393 def appliedname(self, index):
1394 1394 pname = self.applied[index].name
1395 1395 if not self.ui.verbose:
1396 1396 p = pname
1397 1397 else:
1398 1398 p = str(self.series.index(pname)) + " " + pname
1399 1399 return p
1400 1400
1401 1401 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1402 1402 force=None, git=False):
1403 1403 def checkseries(patchname):
1404 1404 if patchname in self.series:
1405 1405 raise util.Abort(_('patch %s is already in the series file')
1406 1406 % patchname)
1407 1407 def checkfile(patchname):
1408 1408 if not force and os.path.exists(self.join(patchname)):
1409 1409 raise util.Abort(_('patch "%s" already exists')
1410 1410 % patchname)
1411 1411
1412 1412 if rev:
1413 1413 if files:
1414 1414 raise util.Abort(_('option "-r" not valid when importing '
1415 1415 'files'))
1416 1416 rev = cmdutil.revrange(repo, rev)
1417 1417 rev.sort(lambda x, y: cmp(y, x))
1418 1418 if (len(files) > 1 or len(rev) > 1) and patchname:
1419 1419 raise util.Abort(_('option "-n" not valid when importing multiple '
1420 1420 'patches'))
1421 1421 i = 0
1422 1422 added = []
1423 1423 if rev:
1424 1424 # If mq patches are applied, we can only import revisions
1425 1425 # that form a linear path to qbase.
1426 1426 # Otherwise, they should form a linear path to a head.
1427 1427 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1428 1428 if len(heads) > 1:
1429 1429 raise util.Abort(_('revision %d is the root of more than one '
1430 1430 'branch') % rev[-1])
1431 1431 if self.applied:
1432 1432 base = revlog.hex(repo.changelog.node(rev[0]))
1433 1433 if base in [n.rev for n in self.applied]:
1434 1434 raise util.Abort(_('revision %d is already managed')
1435 1435 % rev[0])
1436 1436 if heads != [revlog.bin(self.applied[-1].rev)]:
1437 1437 raise util.Abort(_('revision %d is not the parent of '
1438 1438 'the queue') % rev[0])
1439 1439 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1440 1440 lastparent = repo.changelog.parentrevs(base)[0]
1441 1441 else:
1442 1442 if heads != [repo.changelog.node(rev[0])]:
1443 1443 raise util.Abort(_('revision %d has unmanaged children')
1444 1444 % rev[0])
1445 1445 lastparent = None
1446 1446
1447 1447 if git:
1448 1448 self.diffopts().git = True
1449 1449
1450 1450 for r in rev:
1451 1451 p1, p2 = repo.changelog.parentrevs(r)
1452 1452 n = repo.changelog.node(r)
1453 1453 if p2 != revlog.nullrev:
1454 1454 raise util.Abort(_('cannot import merge revision %d') % r)
1455 1455 if lastparent and lastparent != r:
1456 1456 raise util.Abort(_('revision %d is not the parent of %d')
1457 1457 % (r, lastparent))
1458 1458 lastparent = p1
1459 1459
1460 1460 if not patchname:
1461 1461 patchname = normname('%d.diff' % r)
1462 1462 self.check_reserved_name(patchname)
1463 1463 checkseries(patchname)
1464 1464 checkfile(patchname)
1465 1465 self.full_series.insert(0, patchname)
1466 1466
1467 1467 patchf = self.opener(patchname, "w")
1468 1468 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1469 1469 patchf.close()
1470 1470
1471 1471 se = statusentry(revlog.hex(n), patchname)
1472 1472 self.applied.insert(0, se)
1473 1473
1474 1474 added.append(patchname)
1475 1475 patchname = None
1476 1476 self.parse_series()
1477 1477 self.applied_dirty = 1
1478 1478
1479 1479 for filename in files:
1480 1480 if existing:
1481 1481 if filename == '-':
1482 1482 raise util.Abort(_('-e is incompatible with import from -'))
1483 1483 if not patchname:
1484 1484 patchname = normname(filename)
1485 1485 self.check_reserved_name(patchname)
1486 1486 if not os.path.isfile(self.join(patchname)):
1487 1487 raise util.Abort(_("patch %s does not exist") % patchname)
1488 1488 else:
1489 1489 try:
1490 1490 if filename == '-':
1491 1491 if not patchname:
1492 1492 raise util.Abort(_('need --name to import a patch from -'))
1493 1493 text = sys.stdin.read()
1494 1494 else:
1495 1495 text = file(filename, 'rb').read()
1496 1496 except IOError:
1497 1497 raise util.Abort(_("unable to read %s") % patchname)
1498 1498 if not patchname:
1499 1499 patchname = normname(os.path.basename(filename))
1500 1500 self.check_reserved_name(patchname)
1501 1501 checkfile(patchname)
1502 1502 patchf = self.opener(patchname, "w")
1503 1503 patchf.write(text)
1504 1504 checkseries(patchname)
1505 1505 index = self.full_series_end() + i
1506 1506 self.full_series[index:index] = [patchname]
1507 1507 self.parse_series()
1508 1508 self.ui.warn("adding %s to series file\n" % patchname)
1509 1509 i += 1
1510 1510 added.append(patchname)
1511 1511 patchname = None
1512 1512 self.series_dirty = 1
1513 1513 qrepo = self.qrepo()
1514 1514 if qrepo:
1515 1515 qrepo.add(added)
1516 1516
1517 1517 def delete(ui, repo, *patches, **opts):
1518 1518 """remove patches from queue
1519 1519
1520 1520 The patches must not be applied, unless they are arguments to
1521 1521 the --rev parameter. At least one patch or revision is required.
1522 1522
1523 1523 With --rev, mq will stop managing the named revisions (converting
1524 1524 them to regular mercurial changesets). The qfinish command should be
1525 1525 used as an alternative for qdel -r, as the latter option is deprecated.
1526 1526
1527 1527 With --keep, the patch files are preserved in the patch directory."""
1528 1528 q = repo.mq
1529 1529 q.delete(repo, patches, opts)
1530 1530 q.save_dirty()
1531 1531 return 0
1532 1532
1533 1533 def applied(ui, repo, patch=None, **opts):
1534 1534 """print the patches already applied"""
1535 1535 q = repo.mq
1536 1536 if patch:
1537 1537 if patch not in q.series:
1538 1538 raise util.Abort(_("patch %s is not in series file") % patch)
1539 1539 end = q.series.index(patch) + 1
1540 1540 else:
1541 1541 end = q.series_end(True)
1542 1542 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1543 1543
1544 1544 def unapplied(ui, repo, patch=None, **opts):
1545 1545 """print the patches not yet applied"""
1546 1546 q = repo.mq
1547 1547 if patch:
1548 1548 if patch not in q.series:
1549 1549 raise util.Abort(_("patch %s is not in series file") % patch)
1550 1550 start = q.series.index(patch) + 1
1551 1551 else:
1552 1552 start = q.series_end(True)
1553 1553 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1554 1554
1555 1555 def qimport(ui, repo, *filename, **opts):
1556 1556 """import a patch
1557 1557
1558 1558 The patch is inserted into the series after the last applied patch.
1559 1559 If no patches have been applied, qimport prepends the patch
1560 1560 to the series.
1561 1561
1562 1562 The patch will have the same name as its source file unless you
1563 1563 give it a new one with --name.
1564 1564
1565 1565 You can register an existing patch inside the patch directory
1566 1566 with the --existing flag.
1567 1567
1568 1568 With --force, an existing patch of the same name will be overwritten.
1569 1569
1570 1570 An existing changeset may be placed under mq control with --rev
1571 1571 (e.g. qimport --rev tip -n patch will place tip under mq control).
1572 1572 With --git, patches imported with --rev will use the git diff
1573 1573 format.
1574 1574 """
1575 1575 q = repo.mq
1576 1576 q.qimport(repo, filename, patchname=opts['name'],
1577 1577 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1578 1578 git=opts['git'])
1579 1579 q.save_dirty()
1580 1580 return 0
1581 1581
1582 1582 def init(ui, repo, **opts):
1583 1583 """init a new queue repository
1584 1584
1585 1585 The queue repository is unversioned by default. If -c is
1586 1586 specified, qinit will create a separate nested repository
1587 1587 for patches (qinit -c may also be run later to convert
1588 1588 an unversioned patch repository into a versioned one).
1589 1589 You can use qcommit to commit changes to this queue repository."""
1590 1590 q = repo.mq
1591 1591 r = q.init(repo, create=opts['create_repo'])
1592 1592 q.save_dirty()
1593 1593 if r:
1594 1594 if not os.path.exists(r.wjoin('.hgignore')):
1595 1595 fp = r.wopener('.hgignore', 'w')
1596 1596 fp.write('^\\.hg\n')
1597 1597 fp.write('^\\.mq\n')
1598 1598 fp.write('syntax: glob\n')
1599 1599 fp.write('status\n')
1600 1600 fp.write('guards\n')
1601 1601 fp.close()
1602 1602 if not os.path.exists(r.wjoin('series')):
1603 1603 r.wopener('series', 'w').close()
1604 1604 r.add(['.hgignore', 'series'])
1605 1605 commands.add(ui, r)
1606 1606 return 0
1607 1607
1608 1608 def clone(ui, source, dest=None, **opts):
1609 1609 '''clone main and patch repository at same time
1610 1610
1611 1611 If source is local, destination will have no patches applied. If
1612 1612 source is remote, this command can not check if patches are
1613 1613 applied in source, so cannot guarantee that patches are not
1614 1614 applied in destination. If you clone remote repository, be sure
1615 1615 before that it has no patches applied.
1616 1616
1617 1617 Source patch repository is looked for in <src>/.hg/patches by
1618 1618 default. Use -p <url> to change.
1619 1619
1620 1620 The patch directory must be a nested mercurial repository, as
1621 1621 would be created by qinit -c.
1622 1622 '''
1623 1623 def patchdir(repo):
1624 1624 url = repo.url()
1625 1625 if url.endswith('/'):
1626 1626 url = url[:-1]
1627 1627 return url + '/.hg/patches'
1628 1628 cmdutil.setremoteconfig(ui, opts)
1629 1629 if dest is None:
1630 1630 dest = hg.defaultdest(source)
1631 1631 sr = hg.repository(ui, ui.expandpath(source))
1632 1632 patchespath = opts['patches'] or patchdir(sr)
1633 1633 try:
1634 1634 pr = hg.repository(ui, patchespath)
1635 1635 except RepoError:
1636 1636 raise util.Abort(_('versioned patch repository not found'
1637 1637 ' (see qinit -c)'))
1638 1638 qbase, destrev = None, None
1639 1639 if sr.local():
1640 1640 if sr.mq.applied:
1641 1641 qbase = revlog.bin(sr.mq.applied[0].rev)
1642 1642 if not hg.islocal(dest):
1643 1643 heads = dict.fromkeys(sr.heads())
1644 1644 for h in sr.heads(qbase):
1645 1645 del heads[h]
1646 1646 destrev = heads.keys()
1647 1647 destrev.append(sr.changelog.parents(qbase)[0])
1648 1648 elif sr.capable('lookup'):
1649 1649 try:
1650 1650 qbase = sr.lookup('qbase')
1651 1651 except RepoError:
1652 1652 pass
1653 1653 ui.note(_('cloning main repo\n'))
1654 1654 sr, dr = hg.clone(ui, sr.url(), dest,
1655 1655 pull=opts['pull'],
1656 1656 rev=destrev,
1657 1657 update=False,
1658 1658 stream=opts['uncompressed'])
1659 1659 ui.note(_('cloning patch repo\n'))
1660 1660 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1661 1661 pull=opts['pull'], update=not opts['noupdate'],
1662 1662 stream=opts['uncompressed'])
1663 1663 if dr.local():
1664 1664 if qbase:
1665 1665 ui.note(_('stripping applied patches from destination repo\n'))
1666 1666 dr.mq.strip(dr, qbase, update=False, backup=None)
1667 1667 if not opts['noupdate']:
1668 1668 ui.note(_('updating destination repo\n'))
1669 1669 hg.update(dr, dr.changelog.tip())
1670 1670
1671 1671 def commit(ui, repo, *pats, **opts):
1672 1672 """commit changes in the queue repository"""
1673 1673 q = repo.mq
1674 1674 r = q.qrepo()
1675 1675 if not r: raise util.Abort('no queue repository')
1676 1676 commands.commit(r.ui, r, *pats, **opts)
1677 1677
1678 1678 def series(ui, repo, **opts):
1679 1679 """print the entire series file"""
1680 1680 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1681 1681 return 0
1682 1682
1683 1683 def top(ui, repo, **opts):
1684 1684 """print the name of the current patch"""
1685 1685 q = repo.mq
1686 1686 t = q.applied and q.series_end(True) or 0
1687 1687 if t:
1688 1688 return q.qseries(repo, start=t-1, length=1, status='A',
1689 1689 summary=opts.get('summary'))
1690 1690 else:
1691 1691 ui.write("No patches applied\n")
1692 1692 return 1
1693 1693
1694 1694 def next(ui, repo, **opts):
1695 1695 """print the name of the next patch"""
1696 1696 q = repo.mq
1697 1697 end = q.series_end()
1698 1698 if end == len(q.series):
1699 1699 ui.write("All patches applied\n")
1700 1700 return 1
1701 1701 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1702 1702
1703 1703 def prev(ui, repo, **opts):
1704 1704 """print the name of the previous patch"""
1705 1705 q = repo.mq
1706 1706 l = len(q.applied)
1707 1707 if l == 1:
1708 1708 ui.write("Only one patch applied\n")
1709 1709 return 1
1710 1710 if not l:
1711 1711 ui.write("No patches applied\n")
1712 1712 return 1
1713 1713 return q.qseries(repo, start=l-2, length=1, status='A',
1714 1714 summary=opts.get('summary'))
1715 1715
1716 1716 def setupheaderopts(ui, opts):
1717 1717 def do(opt,val):
1718 1718 if not opts[opt] and opts['current' + opt]:
1719 1719 opts[opt] = val
1720 1720 do('user', ui.username())
1721 1721 do('date', "%d %d" % util.makedate())
1722 1722
1723 1723 def new(ui, repo, patch, *args, **opts):
1724 1724 """create a new patch
1725 1725
1726 1726 qnew creates a new patch on top of the currently-applied patch
1727 1727 (if any). It will refuse to run if there are any outstanding
1728 1728 changes unless -f is specified, in which case the patch will
1729 1729 be initialised with them. You may also use -I, -X, and/or a list of
1730 1730 files after the patch name to add only changes to matching files
1731 1731 to the new patch, leaving the rest as uncommitted modifications.
1732 1732
1733 1733 -e, -m or -l set the patch header as well as the commit message.
1734 1734 If none is specified, the patch header is empty and the
1735 1735 commit message is '[mq]: PATCH'"""
1736 1736 q = repo.mq
1737 1737 message = cmdutil.logmessage(opts)
1738 1738 if opts['edit']:
1739 1739 message = ui.edit(message, ui.username())
1740 1740 opts['msg'] = message
1741 1741 setupheaderopts(ui, opts)
1742 1742 q.new(repo, patch, *args, **opts)
1743 1743 q.save_dirty()
1744 1744 return 0
1745 1745
1746 1746 def refresh(ui, repo, *pats, **opts):
1747 1747 """update the current patch
1748 1748
1749 1749 If any file patterns are provided, the refreshed patch will contain only
1750 1750 the modifications that match those patterns; the remaining modifications
1751 1751 will remain in the working directory.
1752 1752
1753 1753 hg add/remove/copy/rename work as usual, though you might want to use
1754 1754 git-style patches (--git or [diff] git=1) to track copies and renames.
1755 1755 """
1756 1756 q = repo.mq
1757 1757 message = cmdutil.logmessage(opts)
1758 1758 if opts['edit']:
1759 1759 if not q.applied:
1760 1760 ui.write(_("No patches applied\n"))
1761 1761 return 1
1762 1762 if message:
1763 1763 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1764 1764 patch = q.applied[-1].name
1765 1765 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1766 1766 message = ui.edit('\n'.join(message), user or ui.username())
1767 1767 setupheaderopts(ui, opts)
1768 1768 ret = q.refresh(repo, pats, msg=message, **opts)
1769 1769 q.save_dirty()
1770 1770 return ret
1771 1771
1772 1772 def diff(ui, repo, *pats, **opts):
1773 1773 """diff of the current patch and subsequent modifications
1774 1774
1775 1775 Shows a diff which includes the current patch as well as any changes which
1776 1776 have been made in the working directory since the last refresh (thus
1777 1777 showing what the current patch would become after a qrefresh).
1778 1778
1779 1779 Use 'hg diff' if you only want to see the changes made since the last
1780 1780 qrefresh, or 'hg export qtip' if you want to see changes made by the
1781 1781 current patch without including changes made since the qrefresh.
1782 1782 """
1783 1783 repo.mq.diff(repo, pats, opts)
1784 1784 return 0
1785 1785
1786 1786 def fold(ui, repo, *files, **opts):
1787 1787 """fold the named patches into the current patch
1788 1788
1789 1789 Patches must not yet be applied. Each patch will be successively
1790 1790 applied to the current patch in the order given. If all the
1791 1791 patches apply successfully, the current patch will be refreshed
1792 1792 with the new cumulative patch, and the folded patches will
1793 1793 be deleted. With -k/--keep, the folded patch files will not
1794 1794 be removed afterwards.
1795 1795
1796 1796 The header for each folded patch will be concatenated with
1797 1797 the current patch header, separated by a line of '* * *'."""
1798 1798
1799 1799 q = repo.mq
1800 1800
1801 1801 if not files:
1802 1802 raise util.Abort(_('qfold requires at least one patch name'))
1803 1803 if not q.check_toppatch(repo):
1804 1804 raise util.Abort(_('No patches applied'))
1805 1805
1806 1806 message = cmdutil.logmessage(opts)
1807 1807 if opts['edit']:
1808 1808 if message:
1809 1809 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1810 1810
1811 1811 parent = q.lookup('qtip')
1812 1812 patches = []
1813 1813 messages = []
1814 1814 for f in files:
1815 1815 p = q.lookup(f)
1816 1816 if p in patches or p == parent:
1817 1817 ui.warn(_('Skipping already folded patch %s') % p)
1818 1818 if q.isapplied(p):
1819 1819 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1820 1820 patches.append(p)
1821 1821
1822 1822 for p in patches:
1823 1823 if not message:
1824 1824 messages.append(q.readheaders(p)[0])
1825 1825 pf = q.join(p)
1826 1826 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1827 1827 if not patchsuccess:
1828 1828 raise util.Abort(_('Error folding patch %s') % p)
1829 1829 patch.updatedir(ui, repo, files)
1830 1830
1831 1831 if not message:
1832 1832 message, comments, user = q.readheaders(parent)[0:3]
1833 1833 for msg in messages:
1834 1834 message.append('* * *')
1835 1835 message.extend(msg)
1836 1836 message = '\n'.join(message)
1837 1837
1838 1838 if opts['edit']:
1839 1839 message = ui.edit(message, user or ui.username())
1840 1840
1841 1841 q.refresh(repo, msg=message)
1842 1842 q.delete(repo, patches, opts)
1843 1843 q.save_dirty()
1844 1844
1845 1845 def goto(ui, repo, patch, **opts):
1846 1846 '''push or pop patches until named patch is at top of stack'''
1847 1847 q = repo.mq
1848 1848 patch = q.lookup(patch)
1849 1849 if q.isapplied(patch):
1850 1850 ret = q.pop(repo, patch, force=opts['force'])
1851 1851 else:
1852 1852 ret = q.push(repo, patch, force=opts['force'])
1853 1853 q.save_dirty()
1854 1854 return ret
1855 1855
1856 1856 def guard(ui, repo, *args, **opts):
1857 1857 '''set or print guards for a patch
1858 1858
1859 1859 Guards control whether a patch can be pushed. A patch with no
1860 1860 guards is always pushed. A patch with a positive guard ("+foo") is
1861 1861 pushed only if the qselect command has activated it. A patch with
1862 1862 a negative guard ("-foo") is never pushed if the qselect command
1863 1863 has activated it.
1864 1864
1865 1865 With no arguments, print the currently active guards.
1866 1866 With arguments, set guards for the named patch.
1867 1867
1868 1868 To set a negative guard "-foo" on topmost patch ("--" is needed so
1869 1869 hg will not interpret "-foo" as an option):
1870 1870 hg qguard -- -foo
1871 1871
1872 1872 To set guards on another patch:
1873 1873 hg qguard other.patch +2.6.17 -stable
1874 1874 '''
1875 1875 def status(idx):
1876 1876 guards = q.series_guards[idx] or ['unguarded']
1877 1877 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1878 1878 q = repo.mq
1879 1879 patch = None
1880 1880 args = list(args)
1881 1881 if opts['list']:
1882 1882 if args or opts['none']:
1883 1883 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1884 1884 for i in xrange(len(q.series)):
1885 1885 status(i)
1886 1886 return
1887 1887 if not args or args[0][0:1] in '-+':
1888 1888 if not q.applied:
1889 1889 raise util.Abort(_('no patches applied'))
1890 1890 patch = q.applied[-1].name
1891 1891 if patch is None and args[0][0:1] not in '-+':
1892 1892 patch = args.pop(0)
1893 1893 if patch is None:
1894 1894 raise util.Abort(_('no patch to work with'))
1895 1895 if args or opts['none']:
1896 1896 idx = q.find_series(patch)
1897 1897 if idx is None:
1898 1898 raise util.Abort(_('no patch named %s') % patch)
1899 1899 q.set_guards(idx, args)
1900 1900 q.save_dirty()
1901 1901 else:
1902 1902 status(q.series.index(q.lookup(patch)))
1903 1903
1904 1904 def header(ui, repo, patch=None):
1905 1905 """Print the header of the topmost or specified patch"""
1906 1906 q = repo.mq
1907 1907
1908 1908 if patch:
1909 1909 patch = q.lookup(patch)
1910 1910 else:
1911 1911 if not q.applied:
1912 1912 ui.write('No patches applied\n')
1913 1913 return 1
1914 1914 patch = q.lookup('qtip')
1915 1915 message = repo.mq.readheaders(patch)[0]
1916 1916
1917 1917 ui.write('\n'.join(message) + '\n')
1918 1918
1919 1919 def lastsavename(path):
1920 1920 (directory, base) = os.path.split(path)
1921 1921 names = os.listdir(directory)
1922 1922 namere = re.compile("%s.([0-9]+)" % base)
1923 1923 maxindex = None
1924 1924 maxname = None
1925 1925 for f in names:
1926 1926 m = namere.match(f)
1927 1927 if m:
1928 1928 index = int(m.group(1))
1929 1929 if maxindex == None or index > maxindex:
1930 1930 maxindex = index
1931 1931 maxname = f
1932 1932 if maxname:
1933 1933 return (os.path.join(directory, maxname), maxindex)
1934 1934 return (None, None)
1935 1935
1936 1936 def savename(path):
1937 1937 (last, index) = lastsavename(path)
1938 1938 if last is None:
1939 1939 index = 0
1940 1940 newpath = path + ".%d" % (index + 1)
1941 1941 return newpath
1942 1942
1943 1943 def push(ui, repo, patch=None, **opts):
1944 1944 """push the next patch onto the stack
1945 1945
1946 1946 When --force is applied, all local changes in patched files will be lost.
1947 1947 """
1948 1948 q = repo.mq
1949 1949 mergeq = None
1950 1950
1951 1951 if opts['all']:
1952 1952 if not q.series:
1953 1953 ui.warn(_('no patches in series\n'))
1954 1954 return 0
1955 1955 patch = q.series[-1]
1956 1956 if opts['merge']:
1957 1957 if opts['name']:
1958 1958 newpath = repo.join(opts['name'])
1959 1959 else:
1960 1960 newpath, i = lastsavename(q.path)
1961 1961 if not newpath:
1962 1962 ui.warn("no saved queues found, please use -n\n")
1963 1963 return 1
1964 1964 mergeq = queue(ui, repo.join(""), newpath)
1965 1965 ui.warn("merging with queue at: %s\n" % mergeq.path)
1966 1966 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1967 1967 mergeq=mergeq)
1968 1968 return ret
1969 1969
1970 1970 def pop(ui, repo, patch=None, **opts):
1971 1971 """pop the current patch off the stack
1972 1972
1973 1973 By default, pops off the top of the patch stack. If given a patch name,
1974 1974 keeps popping off patches until the named patch is at the top of the stack.
1975 1975 """
1976 1976 localupdate = True
1977 1977 if opts['name']:
1978 1978 q = queue(ui, repo.join(""), repo.join(opts['name']))
1979 1979 ui.warn('using patch queue: %s\n' % q.path)
1980 1980 localupdate = False
1981 1981 else:
1982 1982 q = repo.mq
1983 1983 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1984 1984 all=opts['all'])
1985 1985 q.save_dirty()
1986 1986 return ret
1987 1987
1988 1988 def rename(ui, repo, patch, name=None, **opts):
1989 1989 """rename a patch
1990 1990
1991 1991 With one argument, renames the current patch to PATCH1.
1992 1992 With two arguments, renames PATCH1 to PATCH2."""
1993 1993
1994 1994 q = repo.mq
1995 1995
1996 1996 if not name:
1997 1997 name = patch
1998 1998 patch = None
1999 1999
2000 2000 if patch:
2001 2001 patch = q.lookup(patch)
2002 2002 else:
2003 2003 if not q.applied:
2004 2004 ui.write(_('No patches applied\n'))
2005 2005 return
2006 2006 patch = q.lookup('qtip')
2007 2007 absdest = q.join(name)
2008 2008 if os.path.isdir(absdest):
2009 2009 name = normname(os.path.join(name, os.path.basename(patch)))
2010 2010 absdest = q.join(name)
2011 2011 if os.path.exists(absdest):
2012 2012 raise util.Abort(_('%s already exists') % absdest)
2013 2013
2014 2014 if name in q.series:
2015 2015 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2016 2016
2017 2017 if ui.verbose:
2018 2018 ui.write('Renaming %s to %s\n' % (patch, name))
2019 2019 i = q.find_series(patch)
2020 2020 guards = q.guard_re.findall(q.full_series[i])
2021 2021 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2022 2022 q.parse_series()
2023 2023 q.series_dirty = 1
2024 2024
2025 2025 info = q.isapplied(patch)
2026 2026 if info:
2027 2027 q.applied[info[0]] = statusentry(info[1], name)
2028 2028 q.applied_dirty = 1
2029 2029
2030 2030 util.rename(q.join(patch), absdest)
2031 2031 r = q.qrepo()
2032 2032 if r:
2033 2033 wlock = r.wlock()
2034 2034 try:
2035 2035 if r.dirstate[patch] == 'a':
2036 2036 r.dirstate.forget(patch)
2037 2037 r.dirstate.add(name)
2038 2038 else:
2039 2039 if r.dirstate[name] == 'r':
2040 2040 r.undelete([name])
2041 2041 r.copy(patch, name)
2042 2042 r.remove([patch], False)
2043 2043 finally:
2044 2044 del wlock
2045 2045
2046 2046 q.save_dirty()
2047 2047
2048 2048 def restore(ui, repo, rev, **opts):
2049 2049 """restore the queue state saved by a rev"""
2050 2050 rev = repo.lookup(rev)
2051 2051 q = repo.mq
2052 2052 q.restore(repo, rev, delete=opts['delete'],
2053 2053 qupdate=opts['update'])
2054 2054 q.save_dirty()
2055 2055 return 0
2056 2056
2057 2057 def save(ui, repo, **opts):
2058 2058 """save current queue state"""
2059 2059 q = repo.mq
2060 2060 message = cmdutil.logmessage(opts)
2061 2061 ret = q.save(repo, msg=message)
2062 2062 if ret:
2063 2063 return ret
2064 2064 q.save_dirty()
2065 2065 if opts['copy']:
2066 2066 path = q.path
2067 2067 if opts['name']:
2068 2068 newpath = os.path.join(q.basepath, opts['name'])
2069 2069 if os.path.exists(newpath):
2070 2070 if not os.path.isdir(newpath):
2071 2071 raise util.Abort(_('destination %s exists and is not '
2072 2072 'a directory') % newpath)
2073 2073 if not opts['force']:
2074 2074 raise util.Abort(_('destination %s exists, '
2075 2075 'use -f to force') % newpath)
2076 2076 else:
2077 2077 newpath = savename(path)
2078 2078 ui.warn("copy %s to %s\n" % (path, newpath))
2079 2079 util.copyfiles(path, newpath)
2080 2080 if opts['empty']:
2081 2081 try:
2082 2082 os.unlink(q.join(q.status_path))
2083 2083 except:
2084 2084 pass
2085 2085 return 0
2086 2086
2087 2087 def strip(ui, repo, rev, **opts):
2088 2088 """strip a revision and all its descendants from the repository
2089 2089
2090 2090 If one of the working dir's parent revisions is stripped, the working
2091 2091 directory will be updated to the parent of the stripped revision.
2092 2092 """
2093 2093 backup = 'all'
2094 2094 if opts['backup']:
2095 2095 backup = 'strip'
2096 2096 elif opts['nobackup']:
2097 2097 backup = 'none'
2098 2098
2099 2099 rev = repo.lookup(rev)
2100 2100 p = repo.dirstate.parents()
2101 2101 cl = repo.changelog
2102 2102 update = True
2103 2103 if p[0] == revlog.nullid:
2104 2104 update = False
2105 2105 elif p[1] == revlog.nullid and rev != cl.ancestor(p[0], rev):
2106 2106 update = False
2107 2107 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2108 2108 update = False
2109 2109
2110 2110 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2111 2111 return 0
2112 2112
2113 2113 def select(ui, repo, *args, **opts):
2114 2114 '''set or print guarded patches to push
2115 2115
2116 2116 Use the qguard command to set or print guards on patch, then use
2117 2117 qselect to tell mq which guards to use. A patch will be pushed if it
2118 2118 has no guards or any positive guards match the currently selected guard,
2119 2119 but will not be pushed if any negative guards match the current guard.
2120 2120 For example:
2121 2121
2122 2122 qguard foo.patch -stable (negative guard)
2123 2123 qguard bar.patch +stable (positive guard)
2124 2124 qselect stable
2125 2125
2126 2126 This activates the "stable" guard. mq will skip foo.patch (because
2127 2127 it has a negative match) but push bar.patch (because it
2128 2128 has a positive match).
2129 2129
2130 2130 With no arguments, prints the currently active guards.
2131 2131 With one argument, sets the active guard.
2132 2132
2133 2133 Use -n/--none to deactivate guards (no other arguments needed).
2134 2134 When no guards are active, patches with positive guards are skipped
2135 2135 and patches with negative guards are pushed.
2136 2136
2137 2137 qselect can change the guards on applied patches. It does not pop
2138 2138 guarded patches by default. Use --pop to pop back to the last applied
2139 2139 patch that is not guarded. Use --reapply (which implies --pop) to push
2140 2140 back to the current patch afterwards, but skip guarded patches.
2141 2141
2142 2142 Use -s/--series to print a list of all guards in the series file (no
2143 2143 other arguments needed). Use -v for more information.'''
2144 2144
2145 2145 q = repo.mq
2146 2146 guards = q.active()
2147 2147 if args or opts['none']:
2148 2148 old_unapplied = q.unapplied(repo)
2149 2149 old_guarded = [i for i in xrange(len(q.applied)) if
2150 2150 not q.pushable(i)[0]]
2151 2151 q.set_active(args)
2152 2152 q.save_dirty()
2153 2153 if not args:
2154 2154 ui.status(_('guards deactivated\n'))
2155 2155 if not opts['pop'] and not opts['reapply']:
2156 2156 unapplied = q.unapplied(repo)
2157 2157 guarded = [i for i in xrange(len(q.applied))
2158 2158 if not q.pushable(i)[0]]
2159 2159 if len(unapplied) != len(old_unapplied):
2160 2160 ui.status(_('number of unguarded, unapplied patches has '
2161 2161 'changed from %d to %d\n') %
2162 2162 (len(old_unapplied), len(unapplied)))
2163 2163 if len(guarded) != len(old_guarded):
2164 2164 ui.status(_('number of guarded, applied patches has changed '
2165 2165 'from %d to %d\n') %
2166 2166 (len(old_guarded), len(guarded)))
2167 2167 elif opts['series']:
2168 2168 guards = {}
2169 2169 noguards = 0
2170 2170 for gs in q.series_guards:
2171 2171 if not gs:
2172 2172 noguards += 1
2173 2173 for g in gs:
2174 2174 guards.setdefault(g, 0)
2175 2175 guards[g] += 1
2176 2176 if ui.verbose:
2177 2177 guards['NONE'] = noguards
2178 2178 guards = guards.items()
2179 2179 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2180 2180 if guards:
2181 2181 ui.note(_('guards in series file:\n'))
2182 2182 for guard, count in guards:
2183 2183 ui.note('%2d ' % count)
2184 2184 ui.write(guard, '\n')
2185 2185 else:
2186 2186 ui.note(_('no guards in series file\n'))
2187 2187 else:
2188 2188 if guards:
2189 2189 ui.note(_('active guards:\n'))
2190 2190 for g in guards:
2191 2191 ui.write(g, '\n')
2192 2192 else:
2193 2193 ui.write(_('no active guards\n'))
2194 2194 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2195 2195 popped = False
2196 2196 if opts['pop'] or opts['reapply']:
2197 2197 for i in xrange(len(q.applied)):
2198 2198 pushable, reason = q.pushable(i)
2199 2199 if not pushable:
2200 2200 ui.status(_('popping guarded patches\n'))
2201 2201 popped = True
2202 2202 if i == 0:
2203 2203 q.pop(repo, all=True)
2204 2204 else:
2205 2205 q.pop(repo, i-1)
2206 2206 break
2207 2207 if popped:
2208 2208 try:
2209 2209 if reapply:
2210 2210 ui.status(_('reapplying unguarded patches\n'))
2211 2211 q.push(repo, reapply)
2212 2212 finally:
2213 2213 q.save_dirty()
2214 2214
2215 2215 def finish(ui, repo, *revrange, **opts):
2216 2216 """move applied patches into repository history
2217 2217
2218 2218 Finishes the specified revisions (corresponding to applied patches) by
2219 2219 moving them out of mq control into regular repository history.
2220 2220
2221 Accepts a revision range or the --all option. If --all is specified, all
2222 applied mq revisions are removed from mq control. Otherwise, the given
2223 revisions must be at the base of the stack of applied patches.
2221 Accepts a revision range or the --applied option. If --applied is
2222 specified, all applied mq revisions are removed from mq control.
2223 Otherwise, the given revisions must be at the base of the stack of
2224 applied patches.
2224 2225
2225 2226 This can be especially useful if your changes have been applied to an
2226 2227 upstream repository, or if you are about to push your changes to upstream.
2227 2228 """
2228 2229 if not opts['applied'] and not revrange:
2229 2230 raise util.Abort(_('no revisions specified'))
2230 2231 elif opts['applied']:
2231 2232 revrange = ('qbase:qtip',) + revrange
2232 2233
2233 2234 q = repo.mq
2234 2235 if not q.applied:
2235 2236 ui.status(_('no patches applied\n'))
2236 2237 return 0
2237 2238
2238 2239 revs = cmdutil.revrange(repo, revrange)
2239 2240 q.finish(repo, revs)
2240 2241 q.save_dirty()
2241 2242 return 0
2242 2243
2243 2244 def reposetup(ui, repo):
2244 2245 class mqrepo(repo.__class__):
2245 2246 def abort_if_wdir_patched(self, errmsg, force=False):
2246 2247 if self.mq.applied and not force:
2247 2248 parent = revlog.hex(self.dirstate.parents()[0])
2248 2249 if parent in [s.rev for s in self.mq.applied]:
2249 2250 raise util.Abort(errmsg)
2250 2251
2251 2252 def commit(self, *args, **opts):
2252 2253 if len(args) >= 6:
2253 2254 force = args[5]
2254 2255 else:
2255 2256 force = opts.get('force')
2256 2257 self.abort_if_wdir_patched(
2257 2258 _('cannot commit over an applied mq patch'),
2258 2259 force)
2259 2260
2260 2261 return super(mqrepo, self).commit(*args, **opts)
2261 2262
2262 2263 def push(self, remote, force=False, revs=None):
2263 2264 if self.mq.applied and not force and not revs:
2264 2265 raise util.Abort(_('source has mq patches applied'))
2265 2266 return super(mqrepo, self).push(remote, force, revs)
2266 2267
2267 2268 def tags(self):
2268 2269 if self.tagscache:
2269 2270 return self.tagscache
2270 2271
2271 2272 tagscache = super(mqrepo, self).tags()
2272 2273
2273 2274 q = self.mq
2274 2275 if not q.applied:
2275 2276 return tagscache
2276 2277
2277 2278 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2278 2279
2279 2280 if mqtags[-1][0] not in self.changelog.nodemap:
2280 2281 self.ui.warn('mq status file refers to unknown node %s\n'
2281 2282 % revlog.short(mqtags[-1][0]))
2282 2283 return tagscache
2283 2284
2284 2285 mqtags.append((mqtags[-1][0], 'qtip'))
2285 2286 mqtags.append((mqtags[0][0], 'qbase'))
2286 2287 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2287 2288 for patch in mqtags:
2288 2289 if patch[1] in tagscache:
2289 2290 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2290 2291 else:
2291 2292 tagscache[patch[1]] = patch[0]
2292 2293
2293 2294 return tagscache
2294 2295
2295 2296 def _branchtags(self, partial, lrev):
2296 2297 q = self.mq
2297 2298 if not q.applied:
2298 2299 return super(mqrepo, self)._branchtags(partial, lrev)
2299 2300
2300 2301 cl = self.changelog
2301 2302 qbasenode = revlog.bin(q.applied[0].rev)
2302 2303 if qbasenode not in cl.nodemap:
2303 2304 self.ui.warn('mq status file refers to unknown node %s\n'
2304 2305 % revlog.short(qbasenode))
2305 2306 return super(mqrepo, self)._branchtags(partial, lrev)
2306 2307
2307 2308 qbase = cl.rev(qbasenode)
2308 2309 start = lrev + 1
2309 2310 if start < qbase:
2310 2311 # update the cache (excluding the patches) and save it
2311 2312 self._updatebranchcache(partial, lrev+1, qbase)
2312 2313 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2313 2314 start = qbase
2314 2315 # if start = qbase, the cache is as updated as it should be.
2315 2316 # if start > qbase, the cache includes (part of) the patches.
2316 2317 # we might as well use it, but we won't save it.
2317 2318
2318 2319 # update the cache up to the tip
2319 2320 self._updatebranchcache(partial, start, len(cl))
2320 2321
2321 2322 return partial
2322 2323
2323 2324 if repo.local():
2324 2325 repo.__class__ = mqrepo
2325 2326 repo.mq = queue(ui, repo.join(""))
2326 2327
2327 2328 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2328 2329
2329 2330 cmdtable = {
2330 2331 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2331 2332 "qclone":
2332 2333 (clone,
2333 2334 [('', 'pull', None, _('use pull protocol to copy metadata')),
2334 2335 ('U', 'noupdate', None, _('do not update the new working directories')),
2335 2336 ('', 'uncompressed', None,
2336 2337 _('use uncompressed transfer (fast over LAN)')),
2337 2338 ('p', 'patches', '', _('location of source patch repo')),
2338 2339 ] + commands.remoteopts,
2339 2340 _('hg qclone [OPTION]... SOURCE [DEST]')),
2340 2341 "qcommit|qci":
2341 2342 (commit,
2342 2343 commands.table["^commit|ci"][1],
2343 2344 _('hg qcommit [OPTION]... [FILE]...')),
2344 2345 "^qdiff":
2345 2346 (diff,
2346 2347 commands.diffopts + commands.diffopts2 + commands.walkopts,
2347 2348 _('hg qdiff [OPTION]... [FILE]...')),
2348 2349 "qdelete|qremove|qrm":
2349 2350 (delete,
2350 2351 [('k', 'keep', None, _('keep patch file')),
2351 2352 ('r', 'rev', [], _('stop managing a revision'))],
2352 2353 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2353 2354 'qfold':
2354 2355 (fold,
2355 2356 [('e', 'edit', None, _('edit patch header')),
2356 2357 ('k', 'keep', None, _('keep folded patch files')),
2357 2358 ] + commands.commitopts,
2358 2359 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2359 2360 'qgoto':
2360 2361 (goto,
2361 2362 [('f', 'force', None, _('overwrite any local changes'))],
2362 2363 _('hg qgoto [OPTION]... PATCH')),
2363 2364 'qguard':
2364 2365 (guard,
2365 2366 [('l', 'list', None, _('list all patches and guards')),
2366 2367 ('n', 'none', None, _('drop all guards'))],
2367 2368 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2368 2369 'qheader': (header, [], _('hg qheader [PATCH]')),
2369 2370 "^qimport":
2370 2371 (qimport,
2371 2372 [('e', 'existing', None, 'import file in patch dir'),
2372 2373 ('n', 'name', '', 'patch file name'),
2373 2374 ('f', 'force', None, 'overwrite existing files'),
2374 2375 ('r', 'rev', [], 'place existing revisions under mq control'),
2375 2376 ('g', 'git', None, _('use git extended diff format'))],
2376 2377 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2377 2378 "^qinit":
2378 2379 (init,
2379 2380 [('c', 'create-repo', None, 'create queue repository')],
2380 2381 _('hg qinit [-c]')),
2381 2382 "qnew":
2382 2383 (new,
2383 2384 [('e', 'edit', None, _('edit commit message')),
2384 2385 ('f', 'force', None, _('import uncommitted changes into patch')),
2385 2386 ('g', 'git', None, _('use git extended diff format')),
2386 2387 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2387 2388 ('u', 'user', '', _('add "From: <given user>" to patch')),
2388 2389 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2389 2390 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2390 2391 ] + commands.walkopts + commands.commitopts,
2391 2392 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2392 2393 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2393 2394 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2394 2395 "^qpop":
2395 2396 (pop,
2396 2397 [('a', 'all', None, _('pop all patches')),
2397 2398 ('n', 'name', '', _('queue name to pop')),
2398 2399 ('f', 'force', None, _('forget any local changes'))],
2399 2400 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2400 2401 "^qpush":
2401 2402 (push,
2402 2403 [('f', 'force', None, _('apply if the patch has rejects')),
2403 2404 ('l', 'list', None, _('list patch name in commit text')),
2404 2405 ('a', 'all', None, _('apply all patches')),
2405 2406 ('m', 'merge', None, _('merge from another queue')),
2406 2407 ('n', 'name', '', _('merge queue name'))],
2407 2408 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2408 2409 "^qrefresh":
2409 2410 (refresh,
2410 2411 [('e', 'edit', None, _('edit commit message')),
2411 2412 ('g', 'git', None, _('use git extended diff format')),
2412 2413 ('s', 'short', None, _('refresh only files already in the patch')),
2413 2414 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2414 2415 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2415 2416 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2416 2417 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2417 2418 ] + commands.walkopts + commands.commitopts,
2418 2419 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2419 2420 'qrename|qmv':
2420 2421 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2421 2422 "qrestore":
2422 2423 (restore,
2423 2424 [('d', 'delete', None, _('delete save entry')),
2424 2425 ('u', 'update', None, _('update queue working dir'))],
2425 2426 _('hg qrestore [-d] [-u] REV')),
2426 2427 "qsave":
2427 2428 (save,
2428 2429 [('c', 'copy', None, _('copy patch directory')),
2429 2430 ('n', 'name', '', _('copy directory name')),
2430 2431 ('e', 'empty', None, _('clear queue status file')),
2431 2432 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2432 2433 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2433 2434 "qselect":
2434 2435 (select,
2435 2436 [('n', 'none', None, _('disable all guards')),
2436 2437 ('s', 'series', None, _('list all guards in series file')),
2437 2438 ('', 'pop', None, _('pop to before first guarded applied patch')),
2438 2439 ('', 'reapply', None, _('pop, then reapply patches'))],
2439 2440 _('hg qselect [OPTION]... [GUARD]...')),
2440 2441 "qseries":
2441 2442 (series,
2442 2443 [('m', 'missing', None, _('print patches not in series')),
2443 2444 ] + seriesopts,
2444 2445 _('hg qseries [-ms]')),
2445 2446 "^strip":
2446 2447 (strip,
2447 2448 [('f', 'force', None, _('force removal with local changes')),
2448 2449 ('b', 'backup', None, _('bundle unrelated changesets')),
2449 2450 ('n', 'nobackup', None, _('no backups'))],
2450 2451 _('hg strip [-f] [-b] [-n] REV')),
2451 2452 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2452 2453 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2453 2454 "qfinish":
2454 2455 (finish,
2455 2456 [('a', 'applied', None, _('finish all applied changesets'))],
2456 2457 _('hg qfinish [-a] [REV...]')),
2457 2458 }
General Comments 0
You need to be logged in to leave comments. Login now