##// END OF EJS Templates
mq: do not invoke editor until just before patch creation. Closes issue1346.
Brendan Cully -
r7157:fd3cba5e default
parent child Browse files
Show More
@@ -1,2481 +1,2489 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 """options:
645 msg: a string or a no-argument function returning a string
646 """
644 647 msg = opts.get('msg')
645 648 force = opts.get('force')
646 649 user = opts.get('user')
647 650 date = opts.get('date')
648 651 if date:
649 652 date = util.parsedate(date)
650 653 self.check_reserved_name(patch)
651 654 if os.path.exists(self.join(patch)):
652 655 raise util.Abort(_('patch "%s" already exists') % patch)
653 656 if opts.get('include') or opts.get('exclude') or pats:
654 657 match = cmdutil.match(repo, pats, opts)
655 658 m, a, r, d = repo.status(match=match)[:4]
656 659 else:
657 660 m, a, r, d = self.check_localchanges(repo, force)
658 661 match = cmdutil.match(repo, m + a + r)
659 662 commitfiles = m + a + r
660 663 self.check_toppatch(repo)
661 664 wlock = repo.wlock()
662 665 try:
663 666 insert = self.full_series_end()
667 if callable(msg):
668 msg = msg()
664 669 commitmsg = msg and msg or ("[mq]: %s" % patch)
665 670 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
666 671 if n == None:
667 672 raise util.Abort(_("repo commit failed"))
668 673 self.full_series[insert:insert] = [patch]
669 674 self.applied.append(statusentry(revlog.hex(n), patch))
670 675 self.parse_series()
671 676 self.series_dirty = 1
672 677 self.applied_dirty = 1
673 678 p = self.opener(patch, "w")
674 679 if date:
675 680 p.write("# HG changeset patch\n")
676 681 if user:
677 682 p.write("# User " + user + "\n")
678 683 p.write("# Date %d %d\n" % date)
679 684 p.write("\n")
680 685 elif user:
681 686 p.write("From: " + user + "\n")
682 687 p.write("\n")
683 688 if msg:
684 689 msg = msg + "\n"
685 690 p.write(msg)
686 691 p.close()
687 692 wlock = None
688 693 r = self.qrepo()
689 694 if r: r.add([patch])
690 695 if commitfiles:
691 696 self.refresh(repo, short=True, git=opts.get('git'))
692 697 self.removeundo(repo)
693 698 finally:
694 699 del wlock
695 700
696 701 def strip(self, repo, rev, update=True, backup="all", force=None):
697 702 wlock = lock = None
698 703 try:
699 704 wlock = repo.wlock()
700 705 lock = repo.lock()
701 706
702 707 if update:
703 708 self.check_localchanges(repo, force=force, refresh=False)
704 709 urev = self.qparents(repo, rev)
705 710 hg.clean(repo, urev)
706 711 repo.dirstate.write()
707 712
708 713 self.removeundo(repo)
709 714 repair.strip(self.ui, repo, rev, backup)
710 715 # strip may have unbundled a set of backed up revisions after
711 716 # the actual strip
712 717 self.removeundo(repo)
713 718 finally:
714 719 del lock, wlock
715 720
716 721 def isapplied(self, patch):
717 722 """returns (index, rev, patch)"""
718 723 for i in xrange(len(self.applied)):
719 724 a = self.applied[i]
720 725 if a.name == patch:
721 726 return (i, a.rev, a.name)
722 727 return None
723 728
724 729 # if the exact patch name does not exist, we try a few
725 730 # variations. If strict is passed, we try only #1
726 731 #
727 732 # 1) a number to indicate an offset in the series file
728 733 # 2) a unique substring of the patch name was given
729 734 # 3) patchname[-+]num to indicate an offset in the series file
730 735 def lookup(self, patch, strict=False):
731 736 patch = patch and str(patch)
732 737
733 738 def partial_name(s):
734 739 if s in self.series:
735 740 return s
736 741 matches = [x for x in self.series if s in x]
737 742 if len(matches) > 1:
738 743 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
739 744 for m in matches:
740 745 self.ui.warn(' %s\n' % m)
741 746 return None
742 747 if matches:
743 748 return matches[0]
744 749 if len(self.series) > 0 and len(self.applied) > 0:
745 750 if s == 'qtip':
746 751 return self.series[self.series_end(True)-1]
747 752 if s == 'qbase':
748 753 return self.series[0]
749 754 return None
750 755 if patch == None:
751 756 return None
752 757
753 758 # we don't want to return a partial match until we make
754 759 # sure the file name passed in does not exist (checked below)
755 760 res = partial_name(patch)
756 761 if res and res == patch:
757 762 return res
758 763
759 764 if not os.path.isfile(self.join(patch)):
760 765 try:
761 766 sno = int(patch)
762 767 except(ValueError, OverflowError):
763 768 pass
764 769 else:
765 770 if sno < len(self.series):
766 771 return self.series[sno]
767 772 if not strict:
768 773 # return any partial match made above
769 774 if res:
770 775 return res
771 776 minus = patch.rfind('-')
772 777 if minus >= 0:
773 778 res = partial_name(patch[:minus])
774 779 if res:
775 780 i = self.series.index(res)
776 781 try:
777 782 off = int(patch[minus+1:] or 1)
778 783 except(ValueError, OverflowError):
779 784 pass
780 785 else:
781 786 if i - off >= 0:
782 787 return self.series[i - off]
783 788 plus = patch.rfind('+')
784 789 if plus >= 0:
785 790 res = partial_name(patch[:plus])
786 791 if res:
787 792 i = self.series.index(res)
788 793 try:
789 794 off = int(patch[plus+1:] or 1)
790 795 except(ValueError, OverflowError):
791 796 pass
792 797 else:
793 798 if i + off < len(self.series):
794 799 return self.series[i + off]
795 800 raise util.Abort(_("patch %s not in series") % patch)
796 801
797 802 def push(self, repo, patch=None, force=False, list=False,
798 803 mergeq=None):
799 804 wlock = repo.wlock()
800 805 if repo.dirstate.parents()[0] != repo.changelog.tip():
801 806 self.ui.status(_("(working directory not at tip)\n"))
802 807
803 808 try:
804 809 patch = self.lookup(patch)
805 810 # Suppose our series file is: A B C and the current 'top'
806 811 # patch is B. qpush C should be performed (moving forward)
807 812 # qpush B is a NOP (no change) qpush A is an error (can't
808 813 # go backwards with qpush)
809 814 if patch:
810 815 info = self.isapplied(patch)
811 816 if info:
812 817 if info[0] < len(self.applied) - 1:
813 818 raise util.Abort(
814 819 _("cannot push to a previous patch: %s") % patch)
815 820 if info[0] < len(self.series) - 1:
816 821 self.ui.warn(
817 822 _('qpush: %s is already at the top\n') % patch)
818 823 else:
819 824 self.ui.warn(_('all patches are currently applied\n'))
820 825 return
821 826
822 827 # Following the above example, starting at 'top' of B:
823 828 # qpush should be performed (pushes C), but a subsequent
824 829 # qpush without an argument is an error (nothing to
825 830 # apply). This allows a loop of "...while hg qpush..." to
826 831 # work as it detects an error when done
827 832 if self.series_end() == len(self.series):
828 833 self.ui.warn(_('patch series already fully applied\n'))
829 834 return 1
830 835 if not force:
831 836 self.check_localchanges(repo)
832 837
833 838 self.applied_dirty = 1;
834 839 start = self.series_end()
835 840 if start > 0:
836 841 self.check_toppatch(repo)
837 842 if not patch:
838 843 patch = self.series[start]
839 844 end = start + 1
840 845 else:
841 846 end = self.series.index(patch, start) + 1
842 847 s = self.series[start:end]
843 848 all_files = {}
844 849 try:
845 850 if mergeq:
846 851 ret = self.mergepatch(repo, mergeq, s)
847 852 else:
848 853 ret = self.apply(repo, s, list, all_files=all_files)
849 854 except:
850 855 self.ui.warn(_('cleaning up working directory...'))
851 856 node = repo.dirstate.parents()[0]
852 857 hg.revert(repo, node, None)
853 858 unknown = repo.status(unknown=True)[4]
854 859 # only remove unknown files that we know we touched or
855 860 # created while patching
856 861 for f in unknown:
857 862 if f in all_files:
858 863 util.unlink(repo.wjoin(f))
859 864 self.ui.warn(_('done\n'))
860 865 raise
861 866 top = self.applied[-1].name
862 867 if ret[0]:
863 868 self.ui.write(
864 869 "Errors during apply, please fix and refresh %s\n" % top)
865 870 else:
866 871 self.ui.write("Now at: %s\n" % top)
867 872 return ret[0]
868 873 finally:
869 874 del wlock
870 875
871 876 def pop(self, repo, patch=None, force=False, update=True, all=False):
872 877 def getfile(f, rev, flags):
873 878 t = repo.file(f).read(rev)
874 879 repo.wwrite(f, t, flags)
875 880
876 881 wlock = repo.wlock()
877 882 try:
878 883 if patch:
879 884 # index, rev, patch
880 885 info = self.isapplied(patch)
881 886 if not info:
882 887 patch = self.lookup(patch)
883 888 info = self.isapplied(patch)
884 889 if not info:
885 890 raise util.Abort(_("patch %s is not applied") % patch)
886 891
887 892 if len(self.applied) == 0:
888 893 # Allow qpop -a to work repeatedly,
889 894 # but not qpop without an argument
890 895 self.ui.warn(_("no patches applied\n"))
891 896 return not all
892 897
893 898 if not update:
894 899 parents = repo.dirstate.parents()
895 900 rr = [ revlog.bin(x.rev) for x in self.applied ]
896 901 for p in parents:
897 902 if p in rr:
898 903 self.ui.warn(_("qpop: forcing dirstate update\n"))
899 904 update = True
900 905
901 906 if not force and update:
902 907 self.check_localchanges(repo)
903 908
904 909 self.applied_dirty = 1;
905 910 end = len(self.applied)
906 911 if not patch:
907 912 if all:
908 913 popi = 0
909 914 else:
910 915 popi = len(self.applied) - 1
911 916 else:
912 917 popi = info[0] + 1
913 918 if popi >= end:
914 919 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
915 920 return
916 921 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
917 922
918 923 start = info[0]
919 924 rev = revlog.bin(info[1])
920 925
921 926 if update:
922 927 top = self.check_toppatch(repo)
923 928
924 929 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
925 930 raise util.Abort(_("popping would remove a revision not "
926 931 "managed by this patch queue"))
927 932
928 933 # we know there are no local changes, so we can make a simplified
929 934 # form of hg.update.
930 935 if update:
931 936 qp = self.qparents(repo, rev)
932 937 changes = repo.changelog.read(qp)
933 938 mmap = repo.manifest.read(changes[0])
934 939 m, a, r, d = repo.status(qp, top)[:4]
935 940 if d:
936 941 raise util.Abort(_("deletions found between repo revs"))
937 942 for f in m:
938 943 getfile(f, mmap[f], mmap.flags(f))
939 944 for f in r:
940 945 getfile(f, mmap[f], mmap.flags(f))
941 946 for f in m + r:
942 947 repo.dirstate.normal(f)
943 948 for f in a:
944 949 try:
945 950 os.unlink(repo.wjoin(f))
946 951 except OSError, e:
947 952 if e.errno != errno.ENOENT:
948 953 raise
949 954 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
950 955 except: pass
951 956 repo.dirstate.forget(f)
952 957 repo.dirstate.setparents(qp, revlog.nullid)
953 958 del self.applied[start:end]
954 959 self.strip(repo, rev, update=False, backup='strip')
955 960 if len(self.applied):
956 961 self.ui.write(_("Now at: %s\n") % self.applied[-1].name)
957 962 else:
958 963 self.ui.write(_("Patch queue now empty\n"))
959 964 finally:
960 965 del wlock
961 966
962 967 def diff(self, repo, pats, opts):
963 968 top = self.check_toppatch(repo)
964 969 if not top:
965 970 self.ui.write(_("No patches applied\n"))
966 971 return
967 972 qp = self.qparents(repo, top)
968 973 self._diffopts = patch.diffopts(self.ui, opts)
969 974 self.printdiff(repo, qp, files=pats, opts=opts)
970 975
971 976 def refresh(self, repo, pats=None, **opts):
972 977 if len(self.applied) == 0:
973 978 self.ui.write(_("No patches applied\n"))
974 979 return 1
975 980 newdate = opts.get('date')
976 981 if newdate:
977 982 newdate = '%d %d' % util.parsedate(newdate)
978 983 wlock = repo.wlock()
979 984 try:
980 985 self.check_toppatch(repo)
981 986 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
982 987 top = revlog.bin(top)
983 988 if repo.changelog.heads(top) != [top]:
984 989 raise util.Abort(_("cannot refresh a revision with children"))
985 990 cparents = repo.changelog.parents(top)
986 991 patchparent = self.qparents(repo, top)
987 992 message, comments, user, date, patchfound = self.readheaders(patchfn)
988 993
989 994 patchf = self.opener(patchfn, 'r+')
990 995
991 996 # if the patch was a git patch, refresh it as a git patch
992 997 for line in patchf:
993 998 if line.startswith('diff --git'):
994 999 self.diffopts().git = True
995 1000 break
996 1001
997 1002 msg = opts.get('msg', '').rstrip()
998 1003 if msg and comments:
999 1004 # Remove existing message, keeping the rest of the comments
1000 1005 # fields.
1001 1006 # If comments contains 'subject: ', message will prepend
1002 1007 # the field and a blank line.
1003 1008 if message:
1004 1009 subj = 'subject: ' + message[0].lower()
1005 1010 for i in xrange(len(comments)):
1006 1011 if subj == comments[i].lower():
1007 1012 del comments[i]
1008 1013 message = message[2:]
1009 1014 break
1010 1015 ci = 0
1011 1016 for mi in xrange(len(message)):
1012 1017 while message[mi] != comments[ci]:
1013 1018 ci += 1
1014 1019 del comments[ci]
1015 1020
1016 1021 def setheaderfield(comments, prefixes, new):
1017 1022 # Update all references to a field in the patch header.
1018 1023 # If none found, add it email style.
1019 1024 res = False
1020 1025 for prefix in prefixes:
1021 1026 for i in xrange(len(comments)):
1022 1027 if comments[i].startswith(prefix):
1023 1028 comments[i] = prefix + new
1024 1029 res = True
1025 1030 break
1026 1031 return res
1027 1032
1028 1033 newuser = opts.get('user')
1029 1034 if newuser:
1030 1035 if not setheaderfield(comments, ['From: ', '# User '], newuser):
1031 1036 try:
1032 1037 patchheaderat = comments.index('# HG changeset patch')
1033 1038 comments.insert(patchheaderat + 1,'# User ' + newuser)
1034 1039 except ValueError:
1035 1040 comments = ['From: ' + newuser, ''] + comments
1036 1041 user = newuser
1037 1042
1038 1043 if newdate:
1039 1044 if setheaderfield(comments, ['# Date '], newdate):
1040 1045 date = newdate
1041 1046
1042 1047 if msg:
1043 1048 comments.append(msg)
1044 1049
1045 1050 patchf.seek(0)
1046 1051 patchf.truncate()
1047 1052
1048 1053 if comments:
1049 1054 comments = "\n".join(comments) + '\n\n'
1050 1055 patchf.write(comments)
1051 1056
1052 1057 if opts.get('git'):
1053 1058 self.diffopts().git = True
1054 1059 matchfn = cmdutil.match(repo, pats, opts)
1055 1060 tip = repo.changelog.tip()
1056 1061 if top == tip:
1057 1062 # if the top of our patch queue is also the tip, there is an
1058 1063 # optimization here. We update the dirstate in place and strip
1059 1064 # off the tip commit. Then just commit the current directory
1060 1065 # tree. We can also send repo.commit the list of files
1061 1066 # changed to speed up the diff
1062 1067 #
1063 1068 # in short mode, we only diff the files included in the
1064 1069 # patch already
1065 1070 #
1066 1071 # this should really read:
1067 1072 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1068 1073 # but we do it backwards to take advantage of manifest/chlog
1069 1074 # caching against the next repo.status call
1070 1075 #
1071 1076 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1072 1077 changes = repo.changelog.read(tip)
1073 1078 man = repo.manifest.read(changes[0])
1074 1079 aaa = aa[:]
1075 1080 if opts.get('short'):
1076 1081 # if amending a patch, we always match already-in-patch files
1077 1082 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1078 1083 matchfn = match # FIXME: Why have two matchers if we only need one?
1079 1084 else:
1080 1085 match = cmdutil.matchall(repo)
1081 1086 m, a, r, d = repo.status(match=match)[:4]
1082 1087
1083 1088 # we might end up with files that were added between
1084 1089 # tip and the dirstate parent, but then changed in the
1085 1090 # local dirstate. in this case, we want them to only
1086 1091 # show up in the added section
1087 1092 for x in m:
1088 1093 if x not in aa:
1089 1094 mm.append(x)
1090 1095 # we might end up with files added by the local dirstate that
1091 1096 # were deleted by the patch. In this case, they should only
1092 1097 # show up in the changed section.
1093 1098 for x in a:
1094 1099 if x in dd:
1095 1100 del dd[dd.index(x)]
1096 1101 mm.append(x)
1097 1102 else:
1098 1103 aa.append(x)
1099 1104 # make sure any files deleted in the local dirstate
1100 1105 # are not in the add or change column of the patch
1101 1106 forget = []
1102 1107 for x in d + r:
1103 1108 if x in aa:
1104 1109 del aa[aa.index(x)]
1105 1110 forget.append(x)
1106 1111 continue
1107 1112 elif x in mm:
1108 1113 del mm[mm.index(x)]
1109 1114 dd.append(x)
1110 1115
1111 1116 m = util.unique(mm)
1112 1117 r = util.unique(dd)
1113 1118 a = util.unique(aa)
1114 1119 c = [filter(matchfn, l) for l in (m, a, r)]
1115 1120 match = cmdutil.matchfiles(repo, util.unique(c[0] + c[1] + c[2]))
1116 1121 patch.diff(repo, patchparent, match=match,
1117 1122 fp=patchf, changes=c, opts=self.diffopts())
1118 1123 patchf.close()
1119 1124
1120 1125 repo.dirstate.setparents(*cparents)
1121 1126 copies = {}
1122 1127 for dst in a:
1123 1128 src = repo.dirstate.copied(dst)
1124 1129 if src is not None:
1125 1130 copies.setdefault(src, []).append(dst)
1126 1131 repo.dirstate.add(dst)
1127 1132 # remember the copies between patchparent and tip
1128 1133 # this may be slow, so don't do it if we're not tracking copies
1129 1134 if self.diffopts().git:
1130 1135 for dst in aaa:
1131 1136 f = repo.file(dst)
1132 1137 src = f.renamed(man[dst])
1133 1138 if src:
1134 1139 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1135 1140 if dst in a:
1136 1141 copies[src[0]].append(dst)
1137 1142 # we can't copy a file created by the patch itself
1138 1143 if dst in copies:
1139 1144 del copies[dst]
1140 1145 for src, dsts in copies.iteritems():
1141 1146 for dst in dsts:
1142 1147 repo.dirstate.copy(src, dst)
1143 1148 for f in r:
1144 1149 repo.dirstate.remove(f)
1145 1150 # if the patch excludes a modified file, mark that
1146 1151 # file with mtime=0 so status can see it.
1147 1152 mm = []
1148 1153 for i in xrange(len(m)-1, -1, -1):
1149 1154 if not matchfn(m[i]):
1150 1155 mm.append(m[i])
1151 1156 del m[i]
1152 1157 for f in m:
1153 1158 repo.dirstate.normal(f)
1154 1159 for f in mm:
1155 1160 repo.dirstate.normallookup(f)
1156 1161 for f in forget:
1157 1162 repo.dirstate.forget(f)
1158 1163
1159 1164 if not msg:
1160 1165 if not message:
1161 1166 message = "[mq]: %s\n" % patchfn
1162 1167 else:
1163 1168 message = "\n".join(message)
1164 1169 else:
1165 1170 message = msg
1166 1171
1167 1172 if not user:
1168 1173 user = changes[1]
1169 1174
1170 1175 self.applied.pop()
1171 1176 self.applied_dirty = 1
1172 1177 self.strip(repo, top, update=False,
1173 1178 backup='strip')
1174 1179 n = repo.commit(match.files(), message, user, date, match=match,
1175 1180 force=1)
1176 1181 self.applied.append(statusentry(revlog.hex(n), patchfn))
1177 1182 self.removeundo(repo)
1178 1183 else:
1179 1184 self.printdiff(repo, patchparent, fp=patchf)
1180 1185 patchf.close()
1181 1186 added = repo.status()[1]
1182 1187 for a in added:
1183 1188 f = repo.wjoin(a)
1184 1189 try:
1185 1190 os.unlink(f)
1186 1191 except OSError, e:
1187 1192 if e.errno != errno.ENOENT:
1188 1193 raise
1189 1194 try: os.removedirs(os.path.dirname(f))
1190 1195 except: pass
1191 1196 # forget the file copies in the dirstate
1192 1197 # push should readd the files later on
1193 1198 repo.dirstate.forget(a)
1194 1199 self.pop(repo, force=True)
1195 1200 self.push(repo, force=True)
1196 1201 finally:
1197 1202 del wlock
1198 1203
1199 1204 def init(self, repo, create=False):
1200 1205 if not create and os.path.isdir(self.path):
1201 1206 raise util.Abort(_("patch queue directory already exists"))
1202 1207 try:
1203 1208 os.mkdir(self.path)
1204 1209 except OSError, inst:
1205 1210 if inst.errno != errno.EEXIST or not create:
1206 1211 raise
1207 1212 if create:
1208 1213 return self.qrepo(create=True)
1209 1214
1210 1215 def unapplied(self, repo, patch=None):
1211 1216 if patch and patch not in self.series:
1212 1217 raise util.Abort(_("patch %s is not in series file") % patch)
1213 1218 if not patch:
1214 1219 start = self.series_end()
1215 1220 else:
1216 1221 start = self.series.index(patch) + 1
1217 1222 unapplied = []
1218 1223 for i in xrange(start, len(self.series)):
1219 1224 pushable, reason = self.pushable(i)
1220 1225 if pushable:
1221 1226 unapplied.append((i, self.series[i]))
1222 1227 self.explain_pushable(i)
1223 1228 return unapplied
1224 1229
1225 1230 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1226 1231 summary=False):
1227 1232 def displayname(patchname):
1228 1233 if summary:
1229 1234 msg = self.readheaders(patchname)[0]
1230 1235 msg = msg and ': ' + msg[0] or ': '
1231 1236 else:
1232 1237 msg = ''
1233 1238 return '%s%s' % (patchname, msg)
1234 1239
1235 1240 applied = dict.fromkeys([p.name for p in self.applied])
1236 1241 if length is None:
1237 1242 length = len(self.series) - start
1238 1243 if not missing:
1239 1244 for i in xrange(start, start+length):
1240 1245 patch = self.series[i]
1241 1246 if patch in applied:
1242 1247 stat = 'A'
1243 1248 elif self.pushable(i)[0]:
1244 1249 stat = 'U'
1245 1250 else:
1246 1251 stat = 'G'
1247 1252 pfx = ''
1248 1253 if self.ui.verbose:
1249 1254 pfx = '%d %s ' % (i, stat)
1250 1255 elif status and status != stat:
1251 1256 continue
1252 1257 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1253 1258 else:
1254 1259 msng_list = []
1255 1260 for root, dirs, files in os.walk(self.path):
1256 1261 d = root[len(self.path) + 1:]
1257 1262 for f in files:
1258 1263 fl = os.path.join(d, f)
1259 1264 if (fl not in self.series and
1260 1265 fl not in (self.status_path, self.series_path,
1261 1266 self.guards_path)
1262 1267 and not fl.startswith('.')):
1263 1268 msng_list.append(fl)
1264 1269 for x in util.sort(msng_list):
1265 1270 pfx = self.ui.verbose and ('D ') or ''
1266 1271 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1267 1272
1268 1273 def issaveline(self, l):
1269 1274 if l.name == '.hg.patches.save.line':
1270 1275 return True
1271 1276
1272 1277 def qrepo(self, create=False):
1273 1278 if create or os.path.isdir(self.join(".hg")):
1274 1279 return hg.repository(self.ui, path=self.path, create=create)
1275 1280
1276 1281 def restore(self, repo, rev, delete=None, qupdate=None):
1277 1282 c = repo.changelog.read(rev)
1278 1283 desc = c[4].strip()
1279 1284 lines = desc.splitlines()
1280 1285 i = 0
1281 1286 datastart = None
1282 1287 series = []
1283 1288 applied = []
1284 1289 qpp = None
1285 1290 for i in xrange(0, len(lines)):
1286 1291 if lines[i] == 'Patch Data:':
1287 1292 datastart = i + 1
1288 1293 elif lines[i].startswith('Dirstate:'):
1289 1294 l = lines[i].rstrip()
1290 1295 l = l[10:].split(' ')
1291 1296 qpp = [ bin(x) for x in l ]
1292 1297 elif datastart != None:
1293 1298 l = lines[i].rstrip()
1294 1299 se = statusentry(l)
1295 1300 file_ = se.name
1296 1301 if se.rev:
1297 1302 applied.append(se)
1298 1303 else:
1299 1304 series.append(file_)
1300 1305 if datastart == None:
1301 1306 self.ui.warn(_("No saved patch data found\n"))
1302 1307 return 1
1303 1308 self.ui.warn(_("restoring status: %s\n") % lines[0])
1304 1309 self.full_series = series
1305 1310 self.applied = applied
1306 1311 self.parse_series()
1307 1312 self.series_dirty = 1
1308 1313 self.applied_dirty = 1
1309 1314 heads = repo.changelog.heads()
1310 1315 if delete:
1311 1316 if rev not in heads:
1312 1317 self.ui.warn(_("save entry has children, leaving it alone\n"))
1313 1318 else:
1314 1319 self.ui.warn(_("removing save entry %s\n") % short(rev))
1315 1320 pp = repo.dirstate.parents()
1316 1321 if rev in pp:
1317 1322 update = True
1318 1323 else:
1319 1324 update = False
1320 1325 self.strip(repo, rev, update=update, backup='strip')
1321 1326 if qpp:
1322 1327 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1323 1328 (short(qpp[0]), short(qpp[1])))
1324 1329 if qupdate:
1325 1330 self.ui.status(_("queue directory updating\n"))
1326 1331 r = self.qrepo()
1327 1332 if not r:
1328 1333 self.ui.warn(_("Unable to load queue repository\n"))
1329 1334 return 1
1330 1335 hg.clean(r, qpp[0])
1331 1336
1332 1337 def save(self, repo, msg=None):
1333 1338 if len(self.applied) == 0:
1334 1339 self.ui.warn(_("save: no patches applied, exiting\n"))
1335 1340 return 1
1336 1341 if self.issaveline(self.applied[-1]):
1337 1342 self.ui.warn(_("status is already saved\n"))
1338 1343 return 1
1339 1344
1340 1345 ar = [ ':' + x for x in self.full_series ]
1341 1346 if not msg:
1342 1347 msg = _("hg patches saved state")
1343 1348 else:
1344 1349 msg = "hg patches: " + msg.rstrip('\r\n')
1345 1350 r = self.qrepo()
1346 1351 if r:
1347 1352 pp = r.dirstate.parents()
1348 1353 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1349 1354 msg += "\n\nPatch Data:\n"
1350 1355 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1351 1356 "\n".join(ar) + '\n' or "")
1352 1357 n = repo.commit(None, text, user=None, force=1)
1353 1358 if not n:
1354 1359 self.ui.warn(_("repo commit failed\n"))
1355 1360 return 1
1356 1361 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1357 1362 self.applied_dirty = 1
1358 1363 self.removeundo(repo)
1359 1364
1360 1365 def full_series_end(self):
1361 1366 if len(self.applied) > 0:
1362 1367 p = self.applied[-1].name
1363 1368 end = self.find_series(p)
1364 1369 if end == None:
1365 1370 return len(self.full_series)
1366 1371 return end + 1
1367 1372 return 0
1368 1373
1369 1374 def series_end(self, all_patches=False):
1370 1375 """If all_patches is False, return the index of the next pushable patch
1371 1376 in the series, or the series length. If all_patches is True, return the
1372 1377 index of the first patch past the last applied one.
1373 1378 """
1374 1379 end = 0
1375 1380 def next(start):
1376 1381 if all_patches:
1377 1382 return start
1378 1383 i = start
1379 1384 while i < len(self.series):
1380 1385 p, reason = self.pushable(i)
1381 1386 if p:
1382 1387 break
1383 1388 self.explain_pushable(i)
1384 1389 i += 1
1385 1390 return i
1386 1391 if len(self.applied) > 0:
1387 1392 p = self.applied[-1].name
1388 1393 try:
1389 1394 end = self.series.index(p)
1390 1395 except ValueError:
1391 1396 return 0
1392 1397 return next(end + 1)
1393 1398 return next(end)
1394 1399
1395 1400 def appliedname(self, index):
1396 1401 pname = self.applied[index].name
1397 1402 if not self.ui.verbose:
1398 1403 p = pname
1399 1404 else:
1400 1405 p = str(self.series.index(pname)) + " " + pname
1401 1406 return p
1402 1407
1403 1408 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1404 1409 force=None, git=False):
1405 1410 def checkseries(patchname):
1406 1411 if patchname in self.series:
1407 1412 raise util.Abort(_('patch %s is already in the series file')
1408 1413 % patchname)
1409 1414 def checkfile(patchname):
1410 1415 if not force and os.path.exists(self.join(patchname)):
1411 1416 raise util.Abort(_('patch "%s" already exists')
1412 1417 % patchname)
1413 1418
1414 1419 if rev:
1415 1420 if files:
1416 1421 raise util.Abort(_('option "-r" not valid when importing '
1417 1422 'files'))
1418 1423 rev = cmdutil.revrange(repo, rev)
1419 1424 rev.sort(lambda x, y: cmp(y, x))
1420 1425 if (len(files) > 1 or len(rev) > 1) and patchname:
1421 1426 raise util.Abort(_('option "-n" not valid when importing multiple '
1422 1427 'patches'))
1423 1428 i = 0
1424 1429 added = []
1425 1430 if rev:
1426 1431 # If mq patches are applied, we can only import revisions
1427 1432 # that form a linear path to qbase.
1428 1433 # Otherwise, they should form a linear path to a head.
1429 1434 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1430 1435 if len(heads) > 1:
1431 1436 raise util.Abort(_('revision %d is the root of more than one '
1432 1437 'branch') % rev[-1])
1433 1438 if self.applied:
1434 1439 base = revlog.hex(repo.changelog.node(rev[0]))
1435 1440 if base in [n.rev for n in self.applied]:
1436 1441 raise util.Abort(_('revision %d is already managed')
1437 1442 % rev[0])
1438 1443 if heads != [revlog.bin(self.applied[-1].rev)]:
1439 1444 raise util.Abort(_('revision %d is not the parent of '
1440 1445 'the queue') % rev[0])
1441 1446 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1442 1447 lastparent = repo.changelog.parentrevs(base)[0]
1443 1448 else:
1444 1449 if heads != [repo.changelog.node(rev[0])]:
1445 1450 raise util.Abort(_('revision %d has unmanaged children')
1446 1451 % rev[0])
1447 1452 lastparent = None
1448 1453
1449 1454 if git:
1450 1455 self.diffopts().git = True
1451 1456
1452 1457 for r in rev:
1453 1458 p1, p2 = repo.changelog.parentrevs(r)
1454 1459 n = repo.changelog.node(r)
1455 1460 if p2 != revlog.nullrev:
1456 1461 raise util.Abort(_('cannot import merge revision %d') % r)
1457 1462 if lastparent and lastparent != r:
1458 1463 raise util.Abort(_('revision %d is not the parent of %d')
1459 1464 % (r, lastparent))
1460 1465 lastparent = p1
1461 1466
1462 1467 if not patchname:
1463 1468 patchname = normname('%d.diff' % r)
1464 1469 self.check_reserved_name(patchname)
1465 1470 checkseries(patchname)
1466 1471 checkfile(patchname)
1467 1472 self.full_series.insert(0, patchname)
1468 1473
1469 1474 patchf = self.opener(patchname, "w")
1470 1475 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1471 1476 patchf.close()
1472 1477
1473 1478 se = statusentry(revlog.hex(n), patchname)
1474 1479 self.applied.insert(0, se)
1475 1480
1476 1481 added.append(patchname)
1477 1482 patchname = None
1478 1483 self.parse_series()
1479 1484 self.applied_dirty = 1
1480 1485
1481 1486 for filename in files:
1482 1487 if existing:
1483 1488 if filename == '-':
1484 1489 raise util.Abort(_('-e is incompatible with import from -'))
1485 1490 if not patchname:
1486 1491 patchname = normname(filename)
1487 1492 self.check_reserved_name(patchname)
1488 1493 if not os.path.isfile(self.join(patchname)):
1489 1494 raise util.Abort(_("patch %s does not exist") % patchname)
1490 1495 else:
1491 1496 try:
1492 1497 if filename == '-':
1493 1498 if not patchname:
1494 1499 raise util.Abort(_('need --name to import a patch from -'))
1495 1500 text = sys.stdin.read()
1496 1501 else:
1497 1502 text = file(filename, 'rb').read()
1498 1503 except IOError:
1499 1504 raise util.Abort(_("unable to read %s") % filename)
1500 1505 if not patchname:
1501 1506 patchname = normname(os.path.basename(filename))
1502 1507 self.check_reserved_name(patchname)
1503 1508 checkfile(patchname)
1504 1509 patchf = self.opener(patchname, "w")
1505 1510 patchf.write(text)
1506 1511 checkseries(patchname)
1507 1512 index = self.full_series_end() + i
1508 1513 self.full_series[index:index] = [patchname]
1509 1514 self.parse_series()
1510 1515 self.ui.warn("adding %s to series file\n" % patchname)
1511 1516 i += 1
1512 1517 added.append(patchname)
1513 1518 patchname = None
1514 1519 self.series_dirty = 1
1515 1520 qrepo = self.qrepo()
1516 1521 if qrepo:
1517 1522 qrepo.add(added)
1518 1523
1519 1524 def delete(ui, repo, *patches, **opts):
1520 1525 """remove patches from queue
1521 1526
1522 1527 The patches must not be applied, unless they are arguments to
1523 1528 the --rev parameter. At least one patch or revision is required.
1524 1529
1525 1530 With --rev, mq will stop managing the named revisions (converting
1526 1531 them to regular mercurial changesets). The qfinish command should be
1527 1532 used as an alternative for qdel -r, as the latter option is deprecated.
1528 1533
1529 1534 With --keep, the patch files are preserved in the patch directory."""
1530 1535 q = repo.mq
1531 1536 q.delete(repo, patches, opts)
1532 1537 q.save_dirty()
1533 1538 return 0
1534 1539
1535 1540 def applied(ui, repo, patch=None, **opts):
1536 1541 """print the patches already applied"""
1537 1542 q = repo.mq
1538 1543 if patch:
1539 1544 if patch not in q.series:
1540 1545 raise util.Abort(_("patch %s is not in series file") % patch)
1541 1546 end = q.series.index(patch) + 1
1542 1547 else:
1543 1548 end = q.series_end(True)
1544 1549 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1545 1550
1546 1551 def unapplied(ui, repo, patch=None, **opts):
1547 1552 """print the patches not yet applied"""
1548 1553 q = repo.mq
1549 1554 if patch:
1550 1555 if patch not in q.series:
1551 1556 raise util.Abort(_("patch %s is not in series file") % patch)
1552 1557 start = q.series.index(patch) + 1
1553 1558 else:
1554 1559 start = q.series_end(True)
1555 1560 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1556 1561
1557 1562 def qimport(ui, repo, *filename, **opts):
1558 1563 """import a patch
1559 1564
1560 1565 The patch is inserted into the series after the last applied patch.
1561 1566 If no patches have been applied, qimport prepends the patch
1562 1567 to the series.
1563 1568
1564 1569 The patch will have the same name as its source file unless you
1565 1570 give it a new one with --name.
1566 1571
1567 1572 You can register an existing patch inside the patch directory
1568 1573 with the --existing flag.
1569 1574
1570 1575 With --force, an existing patch of the same name will be overwritten.
1571 1576
1572 1577 An existing changeset may be placed under mq control with --rev
1573 1578 (e.g. qimport --rev tip -n patch will place tip under mq control).
1574 1579 With --git, patches imported with --rev will use the git diff
1575 1580 format.
1576 1581 """
1577 1582 q = repo.mq
1578 1583 q.qimport(repo, filename, patchname=opts['name'],
1579 1584 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1580 1585 git=opts['git'])
1581 1586 q.save_dirty()
1582 1587 return 0
1583 1588
1584 1589 def init(ui, repo, **opts):
1585 1590 """init a new queue repository
1586 1591
1587 1592 The queue repository is unversioned by default. If -c is
1588 1593 specified, qinit will create a separate nested repository
1589 1594 for patches (qinit -c may also be run later to convert
1590 1595 an unversioned patch repository into a versioned one).
1591 1596 You can use qcommit to commit changes to this queue repository."""
1592 1597 q = repo.mq
1593 1598 r = q.init(repo, create=opts['create_repo'])
1594 1599 q.save_dirty()
1595 1600 if r:
1596 1601 if not os.path.exists(r.wjoin('.hgignore')):
1597 1602 fp = r.wopener('.hgignore', 'w')
1598 1603 fp.write('^\\.hg\n')
1599 1604 fp.write('^\\.mq\n')
1600 1605 fp.write('syntax: glob\n')
1601 1606 fp.write('status\n')
1602 1607 fp.write('guards\n')
1603 1608 fp.close()
1604 1609 if not os.path.exists(r.wjoin('series')):
1605 1610 r.wopener('series', 'w').close()
1606 1611 r.add(['.hgignore', 'series'])
1607 1612 commands.add(ui, r)
1608 1613 return 0
1609 1614
1610 1615 def clone(ui, source, dest=None, **opts):
1611 1616 '''clone main and patch repository at same time
1612 1617
1613 1618 If source is local, destination will have no patches applied. If
1614 1619 source is remote, this command can not check if patches are
1615 1620 applied in source, so cannot guarantee that patches are not
1616 1621 applied in destination. If you clone remote repository, be sure
1617 1622 before that it has no patches applied.
1618 1623
1619 1624 Source patch repository is looked for in <src>/.hg/patches by
1620 1625 default. Use -p <url> to change.
1621 1626
1622 1627 The patch directory must be a nested mercurial repository, as
1623 1628 would be created by qinit -c.
1624 1629 '''
1625 1630 def patchdir(repo):
1626 1631 url = repo.url()
1627 1632 if url.endswith('/'):
1628 1633 url = url[:-1]
1629 1634 return url + '/.hg/patches'
1630 1635 cmdutil.setremoteconfig(ui, opts)
1631 1636 if dest is None:
1632 1637 dest = hg.defaultdest(source)
1633 1638 sr = hg.repository(ui, ui.expandpath(source))
1634 1639 patchespath = opts['patches'] or patchdir(sr)
1635 1640 try:
1636 1641 pr = hg.repository(ui, patchespath)
1637 1642 except RepoError:
1638 1643 raise util.Abort(_('versioned patch repository not found'
1639 1644 ' (see qinit -c)'))
1640 1645 qbase, destrev = None, None
1641 1646 if sr.local():
1642 1647 if sr.mq.applied:
1643 1648 qbase = revlog.bin(sr.mq.applied[0].rev)
1644 1649 if not hg.islocal(dest):
1645 1650 heads = dict.fromkeys(sr.heads())
1646 1651 for h in sr.heads(qbase):
1647 1652 del heads[h]
1648 1653 destrev = heads.keys()
1649 1654 destrev.append(sr.changelog.parents(qbase)[0])
1650 1655 elif sr.capable('lookup'):
1651 1656 try:
1652 1657 qbase = sr.lookup('qbase')
1653 1658 except RepoError:
1654 1659 pass
1655 1660 ui.note(_('cloning main repo\n'))
1656 1661 sr, dr = hg.clone(ui, sr.url(), dest,
1657 1662 pull=opts['pull'],
1658 1663 rev=destrev,
1659 1664 update=False,
1660 1665 stream=opts['uncompressed'])
1661 1666 ui.note(_('cloning patch repo\n'))
1662 1667 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1663 1668 pull=opts['pull'], update=not opts['noupdate'],
1664 1669 stream=opts['uncompressed'])
1665 1670 if dr.local():
1666 1671 if qbase:
1667 1672 ui.note(_('stripping applied patches from destination repo\n'))
1668 1673 dr.mq.strip(dr, qbase, update=False, backup=None)
1669 1674 if not opts['noupdate']:
1670 1675 ui.note(_('updating destination repo\n'))
1671 1676 hg.update(dr, dr.changelog.tip())
1672 1677
1673 1678 def commit(ui, repo, *pats, **opts):
1674 1679 """commit changes in the queue repository"""
1675 1680 q = repo.mq
1676 1681 r = q.qrepo()
1677 1682 if not r: raise util.Abort('no queue repository')
1678 1683 commands.commit(r.ui, r, *pats, **opts)
1679 1684
1680 1685 def series(ui, repo, **opts):
1681 1686 """print the entire series file"""
1682 1687 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1683 1688 return 0
1684 1689
1685 1690 def top(ui, repo, **opts):
1686 1691 """print the name of the current patch"""
1687 1692 q = repo.mq
1688 1693 t = q.applied and q.series_end(True) or 0
1689 1694 if t:
1690 1695 return q.qseries(repo, start=t-1, length=1, status='A',
1691 1696 summary=opts.get('summary'))
1692 1697 else:
1693 1698 ui.write("No patches applied\n")
1694 1699 return 1
1695 1700
1696 1701 def next(ui, repo, **opts):
1697 1702 """print the name of the next patch"""
1698 1703 q = repo.mq
1699 1704 end = q.series_end()
1700 1705 if end == len(q.series):
1701 1706 ui.write("All patches applied\n")
1702 1707 return 1
1703 1708 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1704 1709
1705 1710 def prev(ui, repo, **opts):
1706 1711 """print the name of the previous patch"""
1707 1712 q = repo.mq
1708 1713 l = len(q.applied)
1709 1714 if l == 1:
1710 1715 ui.write("Only one patch applied\n")
1711 1716 return 1
1712 1717 if not l:
1713 1718 ui.write("No patches applied\n")
1714 1719 return 1
1715 1720 return q.qseries(repo, start=l-2, length=1, status='A',
1716 1721 summary=opts.get('summary'))
1717 1722
1718 1723 def setupheaderopts(ui, opts):
1719 1724 def do(opt,val):
1720 1725 if not opts[opt] and opts['current' + opt]:
1721 1726 opts[opt] = val
1722 1727 do('user', ui.username())
1723 1728 do('date', "%d %d" % util.makedate())
1724 1729
1725 1730 def new(ui, repo, patch, *args, **opts):
1726 1731 """create a new patch
1727 1732
1728 1733 qnew creates a new patch on top of the currently-applied patch
1729 1734 (if any). It will refuse to run if there are any outstanding
1730 1735 changes unless -f is specified, in which case the patch will
1731 1736 be initialised with them. You may also use -I, -X, and/or a list of
1732 1737 files after the patch name to add only changes to matching files
1733 1738 to the new patch, leaving the rest as uncommitted modifications.
1734 1739
1735 1740 -e, -m or -l set the patch header as well as the commit message.
1736 1741 If none is specified, the patch header is empty and the
1737 1742 commit message is '[mq]: PATCH'"""
1743 msg = cmdutil.logmessage(opts)
1744 def getmsg(): return ui.edit(msg, ui.username())
1738 1745 q = repo.mq
1739 message = cmdutil.logmessage(opts)
1740 if opts['edit']:
1741 message = ui.edit(message, ui.username())
1742 opts['msg'] = message
1746 opts['msg'] = msg
1747 if opts.get('edit'):
1748 opts['msg'] = getmsg
1749 else:
1750 opts['msg'] = msg
1743 1751 setupheaderopts(ui, opts)
1744 1752 q.new(repo, patch, *args, **opts)
1745 1753 q.save_dirty()
1746 1754 return 0
1747 1755
1748 1756 def refresh(ui, repo, *pats, **opts):
1749 1757 """update the current patch
1750 1758
1751 1759 If any file patterns are provided, the refreshed patch will contain only
1752 1760 the modifications that match those patterns; the remaining modifications
1753 1761 will remain in the working directory.
1754 1762
1755 1763 If --short is specified, files currently included in the patch will
1756 1764 be refreshed just like matched files and remain in the patch.
1757 1765
1758 1766 hg add/remove/copy/rename work as usual, though you might want to use
1759 1767 git-style patches (--git or [diff] git=1) to track copies and renames.
1760 1768 """
1761 1769 q = repo.mq
1762 1770 message = cmdutil.logmessage(opts)
1763 1771 if opts['edit']:
1764 1772 if not q.applied:
1765 1773 ui.write(_("No patches applied\n"))
1766 1774 return 1
1767 1775 if message:
1768 1776 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1769 1777 patch = q.applied[-1].name
1770 1778 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1771 1779 message = ui.edit('\n'.join(message), user or ui.username())
1772 1780 setupheaderopts(ui, opts)
1773 1781 ret = q.refresh(repo, pats, msg=message, **opts)
1774 1782 q.save_dirty()
1775 1783 return ret
1776 1784
1777 1785 def diff(ui, repo, *pats, **opts):
1778 1786 """diff of the current patch and subsequent modifications
1779 1787
1780 1788 Shows a diff which includes the current patch as well as any changes which
1781 1789 have been made in the working directory since the last refresh (thus
1782 1790 showing what the current patch would become after a qrefresh).
1783 1791
1784 1792 Use 'hg diff' if you only want to see the changes made since the last
1785 1793 qrefresh, or 'hg export qtip' if you want to see changes made by the
1786 1794 current patch without including changes made since the qrefresh.
1787 1795 """
1788 1796 repo.mq.diff(repo, pats, opts)
1789 1797 return 0
1790 1798
1791 1799 def fold(ui, repo, *files, **opts):
1792 1800 """fold the named patches into the current patch
1793 1801
1794 1802 Patches must not yet be applied. Each patch will be successively
1795 1803 applied to the current patch in the order given. If all the
1796 1804 patches apply successfully, the current patch will be refreshed
1797 1805 with the new cumulative patch, and the folded patches will
1798 1806 be deleted. With -k/--keep, the folded patch files will not
1799 1807 be removed afterwards.
1800 1808
1801 1809 The header for each folded patch will be concatenated with
1802 1810 the current patch header, separated by a line of '* * *'."""
1803 1811
1804 1812 q = repo.mq
1805 1813
1806 1814 if not files:
1807 1815 raise util.Abort(_('qfold requires at least one patch name'))
1808 1816 if not q.check_toppatch(repo):
1809 1817 raise util.Abort(_('No patches applied'))
1810 1818
1811 1819 message = cmdutil.logmessage(opts)
1812 1820 if opts['edit']:
1813 1821 if message:
1814 1822 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1815 1823
1816 1824 parent = q.lookup('qtip')
1817 1825 patches = []
1818 1826 messages = []
1819 1827 for f in files:
1820 1828 p = q.lookup(f)
1821 1829 if p in patches or p == parent:
1822 1830 ui.warn(_('Skipping already folded patch %s') % p)
1823 1831 if q.isapplied(p):
1824 1832 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1825 1833 patches.append(p)
1826 1834
1827 1835 for p in patches:
1828 1836 if not message:
1829 1837 messages.append(q.readheaders(p)[0])
1830 1838 pf = q.join(p)
1831 1839 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1832 1840 if not patchsuccess:
1833 1841 raise util.Abort(_('Error folding patch %s') % p)
1834 1842 patch.updatedir(ui, repo, files)
1835 1843
1836 1844 if not message:
1837 1845 message, comments, user = q.readheaders(parent)[0:3]
1838 1846 for msg in messages:
1839 1847 message.append('* * *')
1840 1848 message.extend(msg)
1841 1849 message = '\n'.join(message)
1842 1850
1843 1851 if opts['edit']:
1844 1852 message = ui.edit(message, user or ui.username())
1845 1853
1846 1854 q.refresh(repo, msg=message)
1847 1855 q.delete(repo, patches, opts)
1848 1856 q.save_dirty()
1849 1857
1850 1858 def goto(ui, repo, patch, **opts):
1851 1859 '''push or pop patches until named patch is at top of stack'''
1852 1860 q = repo.mq
1853 1861 patch = q.lookup(patch)
1854 1862 if q.isapplied(patch):
1855 1863 ret = q.pop(repo, patch, force=opts['force'])
1856 1864 else:
1857 1865 ret = q.push(repo, patch, force=opts['force'])
1858 1866 q.save_dirty()
1859 1867 return ret
1860 1868
1861 1869 def guard(ui, repo, *args, **opts):
1862 1870 '''set or print guards for a patch
1863 1871
1864 1872 Guards control whether a patch can be pushed. A patch with no
1865 1873 guards is always pushed. A patch with a positive guard ("+foo") is
1866 1874 pushed only if the qselect command has activated it. A patch with
1867 1875 a negative guard ("-foo") is never pushed if the qselect command
1868 1876 has activated it.
1869 1877
1870 1878 With no arguments, print the currently active guards.
1871 1879 With arguments, set guards for the named patch.
1872 1880
1873 1881 To set a negative guard "-foo" on topmost patch ("--" is needed so
1874 1882 hg will not interpret "-foo" as an option):
1875 1883 hg qguard -- -foo
1876 1884
1877 1885 To set guards on another patch:
1878 1886 hg qguard other.patch +2.6.17 -stable
1879 1887 '''
1880 1888 def status(idx):
1881 1889 guards = q.series_guards[idx] or ['unguarded']
1882 1890 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1883 1891 q = repo.mq
1884 1892 patch = None
1885 1893 args = list(args)
1886 1894 if opts['list']:
1887 1895 if args or opts['none']:
1888 1896 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1889 1897 for i in xrange(len(q.series)):
1890 1898 status(i)
1891 1899 return
1892 1900 if not args or args[0][0:1] in '-+':
1893 1901 if not q.applied:
1894 1902 raise util.Abort(_('no patches applied'))
1895 1903 patch = q.applied[-1].name
1896 1904 if patch is None and args[0][0:1] not in '-+':
1897 1905 patch = args.pop(0)
1898 1906 if patch is None:
1899 1907 raise util.Abort(_('no patch to work with'))
1900 1908 if args or opts['none']:
1901 1909 idx = q.find_series(patch)
1902 1910 if idx is None:
1903 1911 raise util.Abort(_('no patch named %s') % patch)
1904 1912 q.set_guards(idx, args)
1905 1913 q.save_dirty()
1906 1914 else:
1907 1915 status(q.series.index(q.lookup(patch)))
1908 1916
1909 1917 def header(ui, repo, patch=None):
1910 1918 """Print the header of the topmost or specified patch"""
1911 1919 q = repo.mq
1912 1920
1913 1921 if patch:
1914 1922 patch = q.lookup(patch)
1915 1923 else:
1916 1924 if not q.applied:
1917 1925 ui.write('No patches applied\n')
1918 1926 return 1
1919 1927 patch = q.lookup('qtip')
1920 1928 message = repo.mq.readheaders(patch)[0]
1921 1929
1922 1930 ui.write('\n'.join(message) + '\n')
1923 1931
1924 1932 def lastsavename(path):
1925 1933 (directory, base) = os.path.split(path)
1926 1934 names = os.listdir(directory)
1927 1935 namere = re.compile("%s.([0-9]+)" % base)
1928 1936 maxindex = None
1929 1937 maxname = None
1930 1938 for f in names:
1931 1939 m = namere.match(f)
1932 1940 if m:
1933 1941 index = int(m.group(1))
1934 1942 if maxindex == None or index > maxindex:
1935 1943 maxindex = index
1936 1944 maxname = f
1937 1945 if maxname:
1938 1946 return (os.path.join(directory, maxname), maxindex)
1939 1947 return (None, None)
1940 1948
1941 1949 def savename(path):
1942 1950 (last, index) = lastsavename(path)
1943 1951 if last is None:
1944 1952 index = 0
1945 1953 newpath = path + ".%d" % (index + 1)
1946 1954 return newpath
1947 1955
1948 1956 def push(ui, repo, patch=None, **opts):
1949 1957 """push the next patch onto the stack
1950 1958
1951 1959 When --force is applied, all local changes in patched files will be lost.
1952 1960 """
1953 1961 q = repo.mq
1954 1962 mergeq = None
1955 1963
1956 1964 if opts['all']:
1957 1965 if not q.series:
1958 1966 ui.warn(_('no patches in series\n'))
1959 1967 return 0
1960 1968 patch = q.series[-1]
1961 1969 if opts['merge']:
1962 1970 if opts['name']:
1963 1971 newpath = repo.join(opts['name'])
1964 1972 else:
1965 1973 newpath, i = lastsavename(q.path)
1966 1974 if not newpath:
1967 1975 ui.warn(_("no saved queues found, please use -n\n"))
1968 1976 return 1
1969 1977 mergeq = queue(ui, repo.join(""), newpath)
1970 1978 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
1971 1979 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1972 1980 mergeq=mergeq)
1973 1981 return ret
1974 1982
1975 1983 def pop(ui, repo, patch=None, **opts):
1976 1984 """pop the current patch off the stack
1977 1985
1978 1986 By default, pops off the top of the patch stack. If given a patch name,
1979 1987 keeps popping off patches until the named patch is at the top of the stack.
1980 1988 """
1981 1989 localupdate = True
1982 1990 if opts['name']:
1983 1991 q = queue(ui, repo.join(""), repo.join(opts['name']))
1984 1992 ui.warn(_('using patch queue: %s\n') % q.path)
1985 1993 localupdate = False
1986 1994 else:
1987 1995 q = repo.mq
1988 1996 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1989 1997 all=opts['all'])
1990 1998 q.save_dirty()
1991 1999 return ret
1992 2000
1993 2001 def rename(ui, repo, patch, name=None, **opts):
1994 2002 """rename a patch
1995 2003
1996 2004 With one argument, renames the current patch to PATCH1.
1997 2005 With two arguments, renames PATCH1 to PATCH2."""
1998 2006
1999 2007 q = repo.mq
2000 2008
2001 2009 if not name:
2002 2010 name = patch
2003 2011 patch = None
2004 2012
2005 2013 if patch:
2006 2014 patch = q.lookup(patch)
2007 2015 else:
2008 2016 if not q.applied:
2009 2017 ui.write(_('No patches applied\n'))
2010 2018 return
2011 2019 patch = q.lookup('qtip')
2012 2020 absdest = q.join(name)
2013 2021 if os.path.isdir(absdest):
2014 2022 name = normname(os.path.join(name, os.path.basename(patch)))
2015 2023 absdest = q.join(name)
2016 2024 if os.path.exists(absdest):
2017 2025 raise util.Abort(_('%s already exists') % absdest)
2018 2026
2019 2027 if name in q.series:
2020 2028 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2021 2029
2022 2030 if ui.verbose:
2023 2031 ui.write('Renaming %s to %s\n' % (patch, name))
2024 2032 i = q.find_series(patch)
2025 2033 guards = q.guard_re.findall(q.full_series[i])
2026 2034 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2027 2035 q.parse_series()
2028 2036 q.series_dirty = 1
2029 2037
2030 2038 info = q.isapplied(patch)
2031 2039 if info:
2032 2040 q.applied[info[0]] = statusentry(info[1], name)
2033 2041 q.applied_dirty = 1
2034 2042
2035 2043 util.rename(q.join(patch), absdest)
2036 2044 r = q.qrepo()
2037 2045 if r:
2038 2046 wlock = r.wlock()
2039 2047 try:
2040 2048 if r.dirstate[patch] == 'a':
2041 2049 r.dirstate.forget(patch)
2042 2050 r.dirstate.add(name)
2043 2051 else:
2044 2052 if r.dirstate[name] == 'r':
2045 2053 r.undelete([name])
2046 2054 r.copy(patch, name)
2047 2055 r.remove([patch], False)
2048 2056 finally:
2049 2057 del wlock
2050 2058
2051 2059 q.save_dirty()
2052 2060
2053 2061 def restore(ui, repo, rev, **opts):
2054 2062 """restore the queue state saved by a rev"""
2055 2063 rev = repo.lookup(rev)
2056 2064 q = repo.mq
2057 2065 q.restore(repo, rev, delete=opts['delete'],
2058 2066 qupdate=opts['update'])
2059 2067 q.save_dirty()
2060 2068 return 0
2061 2069
2062 2070 def save(ui, repo, **opts):
2063 2071 """save current queue state"""
2064 2072 q = repo.mq
2065 2073 message = cmdutil.logmessage(opts)
2066 2074 ret = q.save(repo, msg=message)
2067 2075 if ret:
2068 2076 return ret
2069 2077 q.save_dirty()
2070 2078 if opts['copy']:
2071 2079 path = q.path
2072 2080 if opts['name']:
2073 2081 newpath = os.path.join(q.basepath, opts['name'])
2074 2082 if os.path.exists(newpath):
2075 2083 if not os.path.isdir(newpath):
2076 2084 raise util.Abort(_('destination %s exists and is not '
2077 2085 'a directory') % newpath)
2078 2086 if not opts['force']:
2079 2087 raise util.Abort(_('destination %s exists, '
2080 2088 'use -f to force') % newpath)
2081 2089 else:
2082 2090 newpath = savename(path)
2083 2091 ui.warn(_("copy %s to %s\n") % (path, newpath))
2084 2092 util.copyfiles(path, newpath)
2085 2093 if opts['empty']:
2086 2094 try:
2087 2095 os.unlink(q.join(q.status_path))
2088 2096 except:
2089 2097 pass
2090 2098 return 0
2091 2099
2092 2100 def strip(ui, repo, rev, **opts):
2093 2101 """strip a revision and all its descendants from the repository
2094 2102
2095 2103 If one of the working dir's parent revisions is stripped, the working
2096 2104 directory will be updated to the parent of the stripped revision.
2097 2105 """
2098 2106 backup = 'all'
2099 2107 if opts['backup']:
2100 2108 backup = 'strip'
2101 2109 elif opts['nobackup']:
2102 2110 backup = 'none'
2103 2111
2104 2112 rev = repo.lookup(rev)
2105 2113 p = repo.dirstate.parents()
2106 2114 cl = repo.changelog
2107 2115 update = True
2108 2116 if p[0] == revlog.nullid:
2109 2117 update = False
2110 2118 elif p[1] == revlog.nullid and rev != cl.ancestor(p[0], rev):
2111 2119 update = False
2112 2120 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2113 2121 update = False
2114 2122
2115 2123 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2116 2124 return 0
2117 2125
2118 2126 def select(ui, repo, *args, **opts):
2119 2127 '''set or print guarded patches to push
2120 2128
2121 2129 Use the qguard command to set or print guards on patch, then use
2122 2130 qselect to tell mq which guards to use. A patch will be pushed if it
2123 2131 has no guards or any positive guards match the currently selected guard,
2124 2132 but will not be pushed if any negative guards match the current guard.
2125 2133 For example:
2126 2134
2127 2135 qguard foo.patch -stable (negative guard)
2128 2136 qguard bar.patch +stable (positive guard)
2129 2137 qselect stable
2130 2138
2131 2139 This activates the "stable" guard. mq will skip foo.patch (because
2132 2140 it has a negative match) but push bar.patch (because it
2133 2141 has a positive match).
2134 2142
2135 2143 With no arguments, prints the currently active guards.
2136 2144 With one argument, sets the active guard.
2137 2145
2138 2146 Use -n/--none to deactivate guards (no other arguments needed).
2139 2147 When no guards are active, patches with positive guards are skipped
2140 2148 and patches with negative guards are pushed.
2141 2149
2142 2150 qselect can change the guards on applied patches. It does not pop
2143 2151 guarded patches by default. Use --pop to pop back to the last applied
2144 2152 patch that is not guarded. Use --reapply (which implies --pop) to push
2145 2153 back to the current patch afterwards, but skip guarded patches.
2146 2154
2147 2155 Use -s/--series to print a list of all guards in the series file (no
2148 2156 other arguments needed). Use -v for more information.'''
2149 2157
2150 2158 q = repo.mq
2151 2159 guards = q.active()
2152 2160 if args or opts['none']:
2153 2161 old_unapplied = q.unapplied(repo)
2154 2162 old_guarded = [i for i in xrange(len(q.applied)) if
2155 2163 not q.pushable(i)[0]]
2156 2164 q.set_active(args)
2157 2165 q.save_dirty()
2158 2166 if not args:
2159 2167 ui.status(_('guards deactivated\n'))
2160 2168 if not opts['pop'] and not opts['reapply']:
2161 2169 unapplied = q.unapplied(repo)
2162 2170 guarded = [i for i in xrange(len(q.applied))
2163 2171 if not q.pushable(i)[0]]
2164 2172 if len(unapplied) != len(old_unapplied):
2165 2173 ui.status(_('number of unguarded, unapplied patches has '
2166 2174 'changed from %d to %d\n') %
2167 2175 (len(old_unapplied), len(unapplied)))
2168 2176 if len(guarded) != len(old_guarded):
2169 2177 ui.status(_('number of guarded, applied patches has changed '
2170 2178 'from %d to %d\n') %
2171 2179 (len(old_guarded), len(guarded)))
2172 2180 elif opts['series']:
2173 2181 guards = {}
2174 2182 noguards = 0
2175 2183 for gs in q.series_guards:
2176 2184 if not gs:
2177 2185 noguards += 1
2178 2186 for g in gs:
2179 2187 guards.setdefault(g, 0)
2180 2188 guards[g] += 1
2181 2189 if ui.verbose:
2182 2190 guards['NONE'] = noguards
2183 2191 guards = guards.items()
2184 2192 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2185 2193 if guards:
2186 2194 ui.note(_('guards in series file:\n'))
2187 2195 for guard, count in guards:
2188 2196 ui.note('%2d ' % count)
2189 2197 ui.write(guard, '\n')
2190 2198 else:
2191 2199 ui.note(_('no guards in series file\n'))
2192 2200 else:
2193 2201 if guards:
2194 2202 ui.note(_('active guards:\n'))
2195 2203 for g in guards:
2196 2204 ui.write(g, '\n')
2197 2205 else:
2198 2206 ui.write(_('no active guards\n'))
2199 2207 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2200 2208 popped = False
2201 2209 if opts['pop'] or opts['reapply']:
2202 2210 for i in xrange(len(q.applied)):
2203 2211 pushable, reason = q.pushable(i)
2204 2212 if not pushable:
2205 2213 ui.status(_('popping guarded patches\n'))
2206 2214 popped = True
2207 2215 if i == 0:
2208 2216 q.pop(repo, all=True)
2209 2217 else:
2210 2218 q.pop(repo, i-1)
2211 2219 break
2212 2220 if popped:
2213 2221 try:
2214 2222 if reapply:
2215 2223 ui.status(_('reapplying unguarded patches\n'))
2216 2224 q.push(repo, reapply)
2217 2225 finally:
2218 2226 q.save_dirty()
2219 2227
2220 2228 def finish(ui, repo, *revrange, **opts):
2221 2229 """move applied patches into repository history
2222 2230
2223 2231 Finishes the specified revisions (corresponding to applied patches) by
2224 2232 moving them out of mq control into regular repository history.
2225 2233
2226 2234 Accepts a revision range or the --applied option. If --applied is
2227 2235 specified, all applied mq revisions are removed from mq control.
2228 2236 Otherwise, the given revisions must be at the base of the stack of
2229 2237 applied patches.
2230 2238
2231 2239 This can be especially useful if your changes have been applied to an
2232 2240 upstream repository, or if you are about to push your changes to upstream.
2233 2241 """
2234 2242 if not opts['applied'] and not revrange:
2235 2243 raise util.Abort(_('no revisions specified'))
2236 2244 elif opts['applied']:
2237 2245 revrange = ('qbase:qtip',) + revrange
2238 2246
2239 2247 q = repo.mq
2240 2248 if not q.applied:
2241 2249 ui.status(_('no patches applied\n'))
2242 2250 return 0
2243 2251
2244 2252 revs = cmdutil.revrange(repo, revrange)
2245 2253 q.finish(repo, revs)
2246 2254 q.save_dirty()
2247 2255 return 0
2248 2256
2249 2257 def reposetup(ui, repo):
2250 2258 class mqrepo(repo.__class__):
2251 2259 def abort_if_wdir_patched(self, errmsg, force=False):
2252 2260 if self.mq.applied and not force:
2253 2261 parent = revlog.hex(self.dirstate.parents()[0])
2254 2262 if parent in [s.rev for s in self.mq.applied]:
2255 2263 raise util.Abort(errmsg)
2256 2264
2257 2265 def commit(self, *args, **opts):
2258 2266 if len(args) >= 6:
2259 2267 force = args[5]
2260 2268 else:
2261 2269 force = opts.get('force')
2262 2270 self.abort_if_wdir_patched(
2263 2271 _('cannot commit over an applied mq patch'),
2264 2272 force)
2265 2273
2266 2274 return super(mqrepo, self).commit(*args, **opts)
2267 2275
2268 2276 def push(self, remote, force=False, revs=None):
2269 2277 if self.mq.applied and not force and not revs:
2270 2278 raise util.Abort(_('source has mq patches applied'))
2271 2279 return super(mqrepo, self).push(remote, force, revs)
2272 2280
2273 2281 def tags(self):
2274 2282 if self.tagscache:
2275 2283 return self.tagscache
2276 2284
2277 2285 tagscache = super(mqrepo, self).tags()
2278 2286
2279 2287 q = self.mq
2280 2288 if not q.applied:
2281 2289 return tagscache
2282 2290
2283 2291 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2284 2292
2285 2293 if mqtags[-1][0] not in self.changelog.nodemap:
2286 2294 self.ui.warn(_('mq status file refers to unknown node %s\n')
2287 2295 % revlog.short(mqtags[-1][0]))
2288 2296 return tagscache
2289 2297
2290 2298 mqtags.append((mqtags[-1][0], 'qtip'))
2291 2299 mqtags.append((mqtags[0][0], 'qbase'))
2292 2300 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2293 2301 for patch in mqtags:
2294 2302 if patch[1] in tagscache:
2295 2303 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2296 2304 % patch[1])
2297 2305 else:
2298 2306 tagscache[patch[1]] = patch[0]
2299 2307
2300 2308 return tagscache
2301 2309
2302 2310 def _branchtags(self, partial, lrev):
2303 2311 q = self.mq
2304 2312 if not q.applied:
2305 2313 return super(mqrepo, self)._branchtags(partial, lrev)
2306 2314
2307 2315 cl = self.changelog
2308 2316 qbasenode = revlog.bin(q.applied[0].rev)
2309 2317 if qbasenode not in cl.nodemap:
2310 2318 self.ui.warn(_('mq status file refers to unknown node %s\n')
2311 2319 % revlog.short(qbasenode))
2312 2320 return super(mqrepo, self)._branchtags(partial, lrev)
2313 2321
2314 2322 qbase = cl.rev(qbasenode)
2315 2323 start = lrev + 1
2316 2324 if start < qbase:
2317 2325 # update the cache (excluding the patches) and save it
2318 2326 self._updatebranchcache(partial, lrev+1, qbase)
2319 2327 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2320 2328 start = qbase
2321 2329 # if start = qbase, the cache is as updated as it should be.
2322 2330 # if start > qbase, the cache includes (part of) the patches.
2323 2331 # we might as well use it, but we won't save it.
2324 2332
2325 2333 # update the cache up to the tip
2326 2334 self._updatebranchcache(partial, start, len(cl))
2327 2335
2328 2336 return partial
2329 2337
2330 2338 if repo.local():
2331 2339 repo.__class__ = mqrepo
2332 2340 repo.mq = queue(ui, repo.join(""))
2333 2341
2334 2342 def uisetup(ui):
2335 2343 # override import to disallow importing over patch
2336 2344 importalias, importcmd = cmdutil.findcmd(ui, 'import', commands.table)
2337 2345 for alias, cmd in commands.table.iteritems():
2338 2346 if cmd is importcmd:
2339 2347 importkey = alias
2340 2348 break
2341 2349 orig_import = importcmd[0]
2342 2350 def mqimport(ui, repo, patch1, *patches, **opts):
2343 2351 if hasattr(repo, 'abort_if_wdir_patched'):
2344 2352 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2345 2353 opts.get('force'))
2346 2354 orig_import(ui, repo, patch1, *patches, **opts)
2347 2355 importcmd = list(importcmd)
2348 2356 importcmd[0] = mqimport
2349 2357 commands.table[importkey] = tuple(importcmd)
2350 2358
2351 2359 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2352 2360
2353 2361 cmdtable = {
2354 2362 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2355 2363 "qclone":
2356 2364 (clone,
2357 2365 [('', 'pull', None, _('use pull protocol to copy metadata')),
2358 2366 ('U', 'noupdate', None, _('do not update the new working directories')),
2359 2367 ('', 'uncompressed', None,
2360 2368 _('use uncompressed transfer (fast over LAN)')),
2361 2369 ('p', 'patches', '', _('location of source patch repo')),
2362 2370 ] + commands.remoteopts,
2363 2371 _('hg qclone [OPTION]... SOURCE [DEST]')),
2364 2372 "qcommit|qci":
2365 2373 (commit,
2366 2374 commands.table["^commit|ci"][1],
2367 2375 _('hg qcommit [OPTION]... [FILE]...')),
2368 2376 "^qdiff":
2369 2377 (diff,
2370 2378 commands.diffopts + commands.diffopts2 + commands.walkopts,
2371 2379 _('hg qdiff [OPTION]... [FILE]...')),
2372 2380 "qdelete|qremove|qrm":
2373 2381 (delete,
2374 2382 [('k', 'keep', None, _('keep patch file')),
2375 2383 ('r', 'rev', [], _('stop managing a revision'))],
2376 2384 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2377 2385 'qfold':
2378 2386 (fold,
2379 2387 [('e', 'edit', None, _('edit patch header')),
2380 2388 ('k', 'keep', None, _('keep folded patch files')),
2381 2389 ] + commands.commitopts,
2382 2390 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2383 2391 'qgoto':
2384 2392 (goto,
2385 2393 [('f', 'force', None, _('overwrite any local changes'))],
2386 2394 _('hg qgoto [OPTION]... PATCH')),
2387 2395 'qguard':
2388 2396 (guard,
2389 2397 [('l', 'list', None, _('list all patches and guards')),
2390 2398 ('n', 'none', None, _('drop all guards'))],
2391 2399 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2392 2400 'qheader': (header, [], _('hg qheader [PATCH]')),
2393 2401 "^qimport":
2394 2402 (qimport,
2395 2403 [('e', 'existing', None, _('import file in patch dir')),
2396 2404 ('n', 'name', '', _('patch file name')),
2397 2405 ('f', 'force', None, _('overwrite existing files')),
2398 2406 ('r', 'rev', [], _('place existing revisions under mq control')),
2399 2407 ('g', 'git', None, _('use git extended diff format'))],
2400 2408 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2401 2409 "^qinit":
2402 2410 (init,
2403 2411 [('c', 'create-repo', None, _('create queue repository'))],
2404 2412 _('hg qinit [-c]')),
2405 2413 "qnew":
2406 2414 (new,
2407 2415 [('e', 'edit', None, _('edit commit message')),
2408 2416 ('f', 'force', None, _('import uncommitted changes into patch')),
2409 2417 ('g', 'git', None, _('use git extended diff format')),
2410 2418 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2411 2419 ('u', 'user', '', _('add "From: <given user>" to patch')),
2412 2420 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2413 2421 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2414 2422 ] + commands.walkopts + commands.commitopts,
2415 2423 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2416 2424 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2417 2425 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2418 2426 "^qpop":
2419 2427 (pop,
2420 2428 [('a', 'all', None, _('pop all patches')),
2421 2429 ('n', 'name', '', _('queue name to pop')),
2422 2430 ('f', 'force', None, _('forget any local changes'))],
2423 2431 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2424 2432 "^qpush":
2425 2433 (push,
2426 2434 [('f', 'force', None, _('apply if the patch has rejects')),
2427 2435 ('l', 'list', None, _('list patch name in commit text')),
2428 2436 ('a', 'all', None, _('apply all patches')),
2429 2437 ('m', 'merge', None, _('merge from another queue')),
2430 2438 ('n', 'name', '', _('merge queue name'))],
2431 2439 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2432 2440 "^qrefresh":
2433 2441 (refresh,
2434 2442 [('e', 'edit', None, _('edit commit message')),
2435 2443 ('g', 'git', None, _('use git extended diff format')),
2436 2444 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2437 2445 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2438 2446 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2439 2447 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2440 2448 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2441 2449 ] + commands.walkopts + commands.commitopts,
2442 2450 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2443 2451 'qrename|qmv':
2444 2452 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2445 2453 "qrestore":
2446 2454 (restore,
2447 2455 [('d', 'delete', None, _('delete save entry')),
2448 2456 ('u', 'update', None, _('update queue working dir'))],
2449 2457 _('hg qrestore [-d] [-u] REV')),
2450 2458 "qsave":
2451 2459 (save,
2452 2460 [('c', 'copy', None, _('copy patch directory')),
2453 2461 ('n', 'name', '', _('copy directory name')),
2454 2462 ('e', 'empty', None, _('clear queue status file')),
2455 2463 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2456 2464 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2457 2465 "qselect":
2458 2466 (select,
2459 2467 [('n', 'none', None, _('disable all guards')),
2460 2468 ('s', 'series', None, _('list all guards in series file')),
2461 2469 ('', 'pop', None, _('pop to before first guarded applied patch')),
2462 2470 ('', 'reapply', None, _('pop, then reapply patches'))],
2463 2471 _('hg qselect [OPTION]... [GUARD]...')),
2464 2472 "qseries":
2465 2473 (series,
2466 2474 [('m', 'missing', None, _('print patches not in series')),
2467 2475 ] + seriesopts,
2468 2476 _('hg qseries [-ms]')),
2469 2477 "^strip":
2470 2478 (strip,
2471 2479 [('f', 'force', None, _('force removal with local changes')),
2472 2480 ('b', 'backup', None, _('bundle unrelated changesets')),
2473 2481 ('n', 'nobackup', None, _('no backups'))],
2474 2482 _('hg strip [-f] [-b] [-n] REV')),
2475 2483 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2476 2484 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2477 2485 "qfinish":
2478 2486 (finish,
2479 2487 [('a', 'applied', None, _('finish all applied changesets'))],
2480 2488 _('hg qfinish [-a] [REV...]')),
2481 2489 }
General Comments 0
You need to be logged in to leave comments. Login now