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