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