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