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