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