##// END OF EJS Templates
qimport: rename patch to patchname to avoid shadowing module
Brendan Cully -
r3133:15fde197 default
parent child Browse files
Show More
@@ -1,2030 +1,2031 b''
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(':', 1)
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, opts):
487 487 realpatches = []
488 488 appliedbase = 0
489 489 forget = opts.get('forget')
490 490 for patch in patches:
491 491 patch = self.lookup(patch, strict=True)
492 492 info = self.isapplied(patch)
493 493 if info and not forget:
494 494 raise util.Abort(_("cannot delete applied patch %s") % patch)
495 495 if patch not in self.series:
496 496 raise util.Abort(_("patch %s not in series file") % patch)
497 497 if forget:
498 498 if not info:
499 499 raise util.Abort(_("cannot forget unapplied patch %s") % patch)
500 500 if info[0] != appliedbase:
501 501 raise util.Abort(_("patch %s not at base") % patch)
502 502 appliedbase += 1
503 503 realpatches.append(patch)
504 504
505 505 if not opts.get('keep'):
506 506 r = self.qrepo()
507 507 if r:
508 508 r.remove(realpatches, True)
509 509 else:
510 510 os.unlink(self.join(patch))
511 511
512 512 if forget:
513 513 del self.applied[:appliedbase]
514 514 self.applied_dirty = 1
515 515 indices = [self.find_series(p) for p in realpatches]
516 516 indices.sort()
517 517 for i in indices[-1::-1]:
518 518 del self.full_series[i]
519 519 self.parse_series()
520 520 self.series_dirty = 1
521 521
522 522 def check_toppatch(self, repo):
523 523 if len(self.applied) > 0:
524 524 top = revlog.bin(self.applied[-1].rev)
525 525 pp = repo.dirstate.parents()
526 526 if top not in pp:
527 527 raise util.Abort(_("queue top not at same revision as working directory"))
528 528 return top
529 529 return None
530 530 def check_localchanges(self, repo, force=False, refresh=True):
531 531 m, a, r, d = repo.status()[:4]
532 532 if m or a or r or d:
533 533 if not force:
534 534 if refresh:
535 535 raise util.Abort(_("local changes found, refresh first"))
536 536 else:
537 537 raise util.Abort(_("local changes found"))
538 538 return m, a, r, d
539 539 def new(self, repo, patch, msg=None, force=None):
540 540 if os.path.exists(self.join(patch)):
541 541 raise util.Abort(_('patch "%s" already exists') % patch)
542 542 m, a, r, d = self.check_localchanges(repo, force)
543 543 commitfiles = m + a + r
544 544 self.check_toppatch(repo)
545 545 wlock = repo.wlock()
546 546 insert = self.full_series_end()
547 547 if msg:
548 548 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
549 549 wlock=wlock)
550 550 else:
551 551 n = repo.commit(commitfiles,
552 552 "New patch: %s" % patch, force=True, wlock=wlock)
553 553 if n == None:
554 554 raise util.Abort(_("repo commit failed"))
555 555 self.full_series[insert:insert] = [patch]
556 556 self.applied.append(statusentry(revlog.hex(n), patch))
557 557 self.parse_series()
558 558 self.series_dirty = 1
559 559 self.applied_dirty = 1
560 560 p = self.opener(patch, "w")
561 561 if msg:
562 562 msg = msg + "\n"
563 563 p.write(msg)
564 564 p.close()
565 565 wlock = None
566 566 r = self.qrepo()
567 567 if r: r.add([patch])
568 568 if commitfiles:
569 569 self.refresh(repo, short=True)
570 570
571 571 def strip(self, repo, rev, update=True, backup="all", wlock=None):
572 572 def limitheads(chlog, stop):
573 573 """return the list of all nodes that have no children"""
574 574 p = {}
575 575 h = []
576 576 stoprev = 0
577 577 if stop in chlog.nodemap:
578 578 stoprev = chlog.rev(stop)
579 579
580 580 for r in range(chlog.count() - 1, -1, -1):
581 581 n = chlog.node(r)
582 582 if n not in p:
583 583 h.append(n)
584 584 if n == stop:
585 585 break
586 586 if r < stoprev:
587 587 break
588 588 for pn in chlog.parents(n):
589 589 p[pn] = 1
590 590 return h
591 591
592 592 def bundle(cg):
593 593 backupdir = repo.join("strip-backup")
594 594 if not os.path.isdir(backupdir):
595 595 os.mkdir(backupdir)
596 596 name = os.path.join(backupdir, "%s" % revlog.short(rev))
597 597 name = savename(name)
598 598 self.ui.warn("saving bundle to %s\n" % name)
599 599 # TODO, exclusive open
600 600 f = open(name, "wb")
601 601 try:
602 602 f.write("HG10")
603 603 z = bz2.BZ2Compressor(9)
604 604 while 1:
605 605 chunk = cg.read(4096)
606 606 if not chunk:
607 607 break
608 608 f.write(z.compress(chunk))
609 609 f.write(z.flush())
610 610 except:
611 611 os.unlink(name)
612 612 raise
613 613 f.close()
614 614 return name
615 615
616 616 def stripall(rev, revnum):
617 617 cl = repo.changelog
618 618 c = cl.read(rev)
619 619 mm = repo.manifest.read(c[0])
620 620 seen = {}
621 621
622 622 for x in xrange(revnum, cl.count()):
623 623 c = cl.read(cl.node(x))
624 624 for f in c[3]:
625 625 if f in seen:
626 626 continue
627 627 seen[f] = 1
628 628 if f in mm:
629 629 filerev = mm[f]
630 630 else:
631 631 filerev = 0
632 632 seen[f] = filerev
633 633 # we go in two steps here so the strip loop happens in a
634 634 # sensible order. When stripping many files, this helps keep
635 635 # our disk access patterns under control.
636 636 seen_list = seen.keys()
637 637 seen_list.sort()
638 638 for f in seen_list:
639 639 ff = repo.file(f)
640 640 filerev = seen[f]
641 641 if filerev != 0:
642 642 if filerev in ff.nodemap:
643 643 filerev = ff.rev(filerev)
644 644 else:
645 645 filerev = 0
646 646 ff.strip(filerev, revnum)
647 647
648 648 if not wlock:
649 649 wlock = repo.wlock()
650 650 lock = repo.lock()
651 651 chlog = repo.changelog
652 652 # TODO delete the undo files, and handle undo of merge sets
653 653 pp = chlog.parents(rev)
654 654 revnum = chlog.rev(rev)
655 655
656 656 if update:
657 657 self.check_localchanges(repo, refresh=False)
658 658 urev = self.qparents(repo, rev)
659 659 hg.clean(repo, urev, wlock=wlock)
660 660 repo.dirstate.write()
661 661
662 662 # save is a list of all the branches we are truncating away
663 663 # that we actually want to keep. changegroup will be used
664 664 # to preserve them and add them back after the truncate
665 665 saveheads = []
666 666 savebases = {}
667 667
668 668 heads = limitheads(chlog, rev)
669 669 seen = {}
670 670
671 671 # search through all the heads, finding those where the revision
672 672 # we want to strip away is an ancestor. Also look for merges
673 673 # that might be turned into new heads by the strip.
674 674 while heads:
675 675 h = heads.pop()
676 676 n = h
677 677 while True:
678 678 seen[n] = 1
679 679 pp = chlog.parents(n)
680 680 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
681 681 if pp[1] not in seen:
682 682 heads.append(pp[1])
683 683 if pp[0] == revlog.nullid:
684 684 break
685 685 if chlog.rev(pp[0]) < revnum:
686 686 break
687 687 n = pp[0]
688 688 if n == rev:
689 689 break
690 690 r = chlog.reachable(h, rev)
691 691 if rev not in r:
692 692 saveheads.append(h)
693 693 for x in r:
694 694 if chlog.rev(x) > revnum:
695 695 savebases[x] = 1
696 696
697 697 # create a changegroup for all the branches we need to keep
698 698 if backup == "all":
699 699 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
700 700 bundle(backupch)
701 701 if saveheads:
702 702 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
703 703 chgrpfile = bundle(backupch)
704 704
705 705 stripall(rev, revnum)
706 706
707 707 change = chlog.read(rev)
708 708 chlog.strip(revnum, revnum)
709 709 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
710 710 if saveheads:
711 711 self.ui.status("adding branch\n")
712 712 commands.unbundle(self.ui, repo, chgrpfile, update=False)
713 713 if backup != "strip":
714 714 os.unlink(chgrpfile)
715 715
716 716 def isapplied(self, patch):
717 717 """returns (index, rev, patch)"""
718 718 for i in xrange(len(self.applied)):
719 719 a = self.applied[i]
720 720 if a.name == patch:
721 721 return (i, a.rev, a.name)
722 722 return None
723 723
724 724 # if the exact patch name does not exist, we try a few
725 725 # variations. If strict is passed, we try only #1
726 726 #
727 727 # 1) a number to indicate an offset in the series file
728 728 # 2) a unique substring of the patch name was given
729 729 # 3) patchname[-+]num to indicate an offset in the series file
730 730 def lookup(self, patch, strict=False):
731 731 patch = patch and str(patch)
732 732
733 733 def partial_name(s):
734 734 if s in self.series:
735 735 return s
736 736 matches = [x for x in self.series if s in x]
737 737 if len(matches) > 1:
738 738 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
739 739 for m in matches:
740 740 self.ui.warn(' %s\n' % m)
741 741 return None
742 742 if matches:
743 743 return matches[0]
744 744 if len(self.series) > 0 and len(self.applied) > 0:
745 745 if s == 'qtip':
746 746 return self.series[self.series_end()-1]
747 747 if s == 'qbase':
748 748 return self.series[0]
749 749 return None
750 750 if patch == None:
751 751 return None
752 752
753 753 # we don't want to return a partial match until we make
754 754 # sure the file name passed in does not exist (checked below)
755 755 res = partial_name(patch)
756 756 if res and res == patch:
757 757 return res
758 758
759 759 if not os.path.isfile(self.join(patch)):
760 760 try:
761 761 sno = int(patch)
762 762 except(ValueError, OverflowError):
763 763 pass
764 764 else:
765 765 if sno < len(self.series):
766 766 return self.series[sno]
767 767 if not strict:
768 768 # return any partial match made above
769 769 if res:
770 770 return res
771 771 minus = patch.rfind('-')
772 772 if minus >= 0:
773 773 res = partial_name(patch[:minus])
774 774 if res:
775 775 i = self.series.index(res)
776 776 try:
777 777 off = int(patch[minus+1:] or 1)
778 778 except(ValueError, OverflowError):
779 779 pass
780 780 else:
781 781 if i - off >= 0:
782 782 return self.series[i - off]
783 783 plus = patch.rfind('+')
784 784 if plus >= 0:
785 785 res = partial_name(patch[:plus])
786 786 if res:
787 787 i = self.series.index(res)
788 788 try:
789 789 off = int(patch[plus+1:] or 1)
790 790 except(ValueError, OverflowError):
791 791 pass
792 792 else:
793 793 if i + off < len(self.series):
794 794 return self.series[i + off]
795 795 raise util.Abort(_("patch %s not in series") % patch)
796 796
797 797 def push(self, repo, patch=None, force=False, list=False,
798 798 mergeq=None, wlock=None):
799 799 if not wlock:
800 800 wlock = repo.wlock()
801 801 patch = self.lookup(patch)
802 802 if patch and self.isapplied(patch):
803 803 self.ui.warn(_("patch %s is already applied\n") % patch)
804 804 sys.exit(1)
805 805 if self.series_end() == len(self.series):
806 806 self.ui.warn(_("patch series fully applied\n"))
807 807 sys.exit(1)
808 808 if not force:
809 809 self.check_localchanges(repo)
810 810
811 811 self.applied_dirty = 1;
812 812 start = self.series_end()
813 813 if start > 0:
814 814 self.check_toppatch(repo)
815 815 if not patch:
816 816 patch = self.series[start]
817 817 end = start + 1
818 818 else:
819 819 end = self.series.index(patch, start) + 1
820 820 s = self.series[start:end]
821 821 if mergeq:
822 822 ret = self.mergepatch(repo, mergeq, s, wlock)
823 823 else:
824 824 ret = self.apply(repo, s, list, wlock=wlock)
825 825 top = self.applied[-1].name
826 826 if ret[0]:
827 827 self.ui.write("Errors during apply, please fix and refresh %s\n" %
828 828 top)
829 829 else:
830 830 self.ui.write("Now at: %s\n" % top)
831 831 return ret[0]
832 832
833 833 def pop(self, repo, patch=None, force=False, update=True, all=False,
834 834 wlock=None):
835 835 def getfile(f, rev):
836 836 t = repo.file(f).read(rev)
837 837 try:
838 838 repo.wfile(f, "w").write(t)
839 839 except IOError:
840 840 try:
841 841 os.makedirs(os.path.dirname(repo.wjoin(f)))
842 842 except OSError, err:
843 843 if err.errno != errno.EEXIST: raise
844 844 repo.wfile(f, "w").write(t)
845 845
846 846 if not wlock:
847 847 wlock = repo.wlock()
848 848 if patch:
849 849 # index, rev, patch
850 850 info = self.isapplied(patch)
851 851 if not info:
852 852 patch = self.lookup(patch)
853 853 info = self.isapplied(patch)
854 854 if not info:
855 855 raise util.Abort(_("patch %s is not applied") % patch)
856 856 if len(self.applied) == 0:
857 857 self.ui.warn(_("no patches applied\n"))
858 858 sys.exit(1)
859 859
860 860 if not update:
861 861 parents = repo.dirstate.parents()
862 862 rr = [ revlog.bin(x.rev) for x in self.applied ]
863 863 for p in parents:
864 864 if p in rr:
865 865 self.ui.warn("qpop: forcing dirstate update\n")
866 866 update = True
867 867
868 868 if not force and update:
869 869 self.check_localchanges(repo)
870 870
871 871 self.applied_dirty = 1;
872 872 end = len(self.applied)
873 873 if not patch:
874 874 if all:
875 875 popi = 0
876 876 else:
877 877 popi = len(self.applied) - 1
878 878 else:
879 879 popi = info[0] + 1
880 880 if popi >= end:
881 881 self.ui.warn("qpop: %s is already at the top\n" % patch)
882 882 return
883 883 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
884 884
885 885 start = info[0]
886 886 rev = revlog.bin(info[1])
887 887
888 888 # we know there are no local changes, so we can make a simplified
889 889 # form of hg.update.
890 890 if update:
891 891 top = self.check_toppatch(repo)
892 892 qp = self.qparents(repo, rev)
893 893 changes = repo.changelog.read(qp)
894 894 mmap = repo.manifest.read(changes[0])
895 895 m, a, r, d, u = repo.status(qp, top)[:5]
896 896 if d:
897 897 raise util.Abort("deletions found between repo revs")
898 898 for f in m:
899 899 getfile(f, mmap[f])
900 900 for f in r:
901 901 getfile(f, mmap[f])
902 902 util.set_exec(repo.wjoin(f), mmap.execf(f))
903 903 repo.dirstate.update(m + r, 'n')
904 904 for f in a:
905 905 try: os.unlink(repo.wjoin(f))
906 906 except: raise
907 907 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
908 908 except: pass
909 909 if a:
910 910 repo.dirstate.forget(a)
911 911 repo.dirstate.setparents(qp, revlog.nullid)
912 912 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
913 913 del self.applied[start:end]
914 914 if len(self.applied):
915 915 self.ui.write("Now at: %s\n" % self.applied[-1].name)
916 916 else:
917 917 self.ui.write("Patch queue now empty\n")
918 918
919 919 def diff(self, repo, pats, opts):
920 920 top = self.check_toppatch(repo)
921 921 if not top:
922 922 self.ui.write("No patches applied\n")
923 923 return
924 924 qp = self.qparents(repo, top)
925 925 self.printdiff(repo, qp, files=pats, opts=opts)
926 926
927 927 def refresh(self, repo, pats=None, **opts):
928 928 if len(self.applied) == 0:
929 929 self.ui.write("No patches applied\n")
930 930 return 1
931 931 wlock = repo.wlock()
932 932 self.check_toppatch(repo)
933 933 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
934 934 top = revlog.bin(top)
935 935 cparents = repo.changelog.parents(top)
936 936 patchparent = self.qparents(repo, top)
937 937 message, comments, user, date, patchfound = self.readheaders(patchfn)
938 938
939 939 patchf = self.opener(patchfn, "w")
940 940 msg = opts.get('msg', '').rstrip()
941 941 if msg:
942 942 if comments:
943 943 # Remove existing message.
944 944 ci = 0
945 945 for mi in range(len(message)):
946 946 while message[mi] != comments[ci]:
947 947 ci += 1
948 948 del comments[ci]
949 949 comments.append(msg)
950 950 if comments:
951 951 comments = "\n".join(comments) + '\n\n'
952 952 patchf.write(comments)
953 953
954 954 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
955 955 tip = repo.changelog.tip()
956 956 if top == tip:
957 957 # if the top of our patch queue is also the tip, there is an
958 958 # optimization here. We update the dirstate in place and strip
959 959 # off the tip commit. Then just commit the current directory
960 960 # tree. We can also send repo.commit the list of files
961 961 # changed to speed up the diff
962 962 #
963 963 # in short mode, we only diff the files included in the
964 964 # patch already
965 965 #
966 966 # this should really read:
967 967 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
968 968 # but we do it backwards to take advantage of manifest/chlog
969 969 # caching against the next repo.status call
970 970 #
971 971 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
972 972 if opts.get('short'):
973 973 filelist = mm + aa + dd
974 974 else:
975 975 filelist = None
976 976 m, a, r, d, u = repo.status(files=filelist)[:5]
977 977
978 978 # we might end up with files that were added between tip and
979 979 # the dirstate parent, but then changed in the local dirstate.
980 980 # in this case, we want them to only show up in the added section
981 981 for x in m:
982 982 if x not in aa:
983 983 mm.append(x)
984 984 # we might end up with files added by the local dirstate that
985 985 # were deleted by the patch. In this case, they should only
986 986 # show up in the changed section.
987 987 for x in a:
988 988 if x in dd:
989 989 del dd[dd.index(x)]
990 990 mm.append(x)
991 991 else:
992 992 aa.append(x)
993 993 # make sure any files deleted in the local dirstate
994 994 # are not in the add or change column of the patch
995 995 forget = []
996 996 for x in d + r:
997 997 if x in aa:
998 998 del aa[aa.index(x)]
999 999 forget.append(x)
1000 1000 continue
1001 1001 elif x in mm:
1002 1002 del mm[mm.index(x)]
1003 1003 dd.append(x)
1004 1004
1005 1005 m = list(util.unique(mm))
1006 1006 r = list(util.unique(dd))
1007 1007 a = list(util.unique(aa))
1008 1008 filelist = filter(matchfn, util.unique(m + r + a))
1009 1009 if opts.get('git'):
1010 1010 self.diffopts().git = True
1011 1011 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1012 1012 fp=patchf, changes=(m, a, r, [], u),
1013 1013 opts=self.diffopts())
1014 1014 patchf.close()
1015 1015
1016 1016 changes = repo.changelog.read(tip)
1017 1017 repo.dirstate.setparents(*cparents)
1018 1018 copies = [(f, repo.dirstate.copied(f)) for f in a]
1019 1019 repo.dirstate.update(a, 'a')
1020 1020 for dst, src in copies:
1021 1021 repo.dirstate.copy(src, dst)
1022 1022 repo.dirstate.update(r, 'r')
1023 1023 # if the patch excludes a modified file, mark that file with mtime=0
1024 1024 # so status can see it.
1025 1025 mm = []
1026 1026 for i in range(len(m)-1, -1, -1):
1027 1027 if not matchfn(m[i]):
1028 1028 mm.append(m[i])
1029 1029 del m[i]
1030 1030 repo.dirstate.update(m, 'n')
1031 1031 repo.dirstate.update(mm, 'n', st_mtime=0)
1032 1032 repo.dirstate.forget(forget)
1033 1033
1034 1034 if not msg:
1035 1035 if not message:
1036 1036 message = "patch queue: %s\n" % patchfn
1037 1037 else:
1038 1038 message = "\n".join(message)
1039 1039 else:
1040 1040 message = msg
1041 1041
1042 1042 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1043 1043 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1044 1044 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1045 1045 self.applied_dirty = 1
1046 1046 else:
1047 1047 self.printdiff(repo, patchparent, fp=patchf)
1048 1048 patchf.close()
1049 1049 self.pop(repo, force=True, wlock=wlock)
1050 1050 self.push(repo, force=True, wlock=wlock)
1051 1051
1052 1052 def init(self, repo, create=False):
1053 1053 if os.path.isdir(self.path):
1054 1054 raise util.Abort(_("patch queue directory already exists"))
1055 1055 os.mkdir(self.path)
1056 1056 if create:
1057 1057 return self.qrepo(create=True)
1058 1058
1059 1059 def unapplied(self, repo, patch=None):
1060 1060 if patch and patch not in self.series:
1061 1061 raise util.Abort(_("patch %s is not in series file") % patch)
1062 1062 if not patch:
1063 1063 start = self.series_end()
1064 1064 else:
1065 1065 start = self.series.index(patch) + 1
1066 1066 unapplied = []
1067 1067 for i in xrange(start, len(self.series)):
1068 1068 pushable, reason = self.pushable(i)
1069 1069 if pushable:
1070 1070 unapplied.append((i, self.series[i]))
1071 1071 self.explain_pushable(i)
1072 1072 return unapplied
1073 1073
1074 1074 def qseries(self, repo, missing=None, summary=False):
1075 1075 start = self.series_end(all_patches=True)
1076 1076 if not missing:
1077 1077 for i in range(len(self.series)):
1078 1078 patch = self.series[i]
1079 1079 if self.ui.verbose:
1080 1080 if i < start:
1081 1081 status = 'A'
1082 1082 elif self.pushable(i)[0]:
1083 1083 status = 'U'
1084 1084 else:
1085 1085 status = 'G'
1086 1086 self.ui.write('%d %s ' % (i, status))
1087 1087 if summary:
1088 1088 msg = self.readheaders(patch)[0]
1089 1089 msg = msg and ': ' + msg[0] or ': '
1090 1090 else:
1091 1091 msg = ''
1092 1092 self.ui.write('%s%s\n' % (patch, msg))
1093 1093 else:
1094 1094 msng_list = []
1095 1095 for root, dirs, files in os.walk(self.path):
1096 1096 d = root[len(self.path) + 1:]
1097 1097 for f in files:
1098 1098 fl = os.path.join(d, f)
1099 1099 if (fl not in self.series and
1100 1100 fl not in (self.status_path, self.series_path)
1101 1101 and not fl.startswith('.')):
1102 1102 msng_list.append(fl)
1103 1103 msng_list.sort()
1104 1104 for x in msng_list:
1105 1105 if self.ui.verbose:
1106 1106 self.ui.write("D ")
1107 1107 self.ui.write("%s\n" % x)
1108 1108
1109 1109 def issaveline(self, l):
1110 1110 if l.name == '.hg.patches.save.line':
1111 1111 return True
1112 1112
1113 1113 def qrepo(self, create=False):
1114 1114 if create or os.path.isdir(self.join(".hg")):
1115 1115 return hg.repository(self.ui, path=self.path, create=create)
1116 1116
1117 1117 def restore(self, repo, rev, delete=None, qupdate=None):
1118 1118 c = repo.changelog.read(rev)
1119 1119 desc = c[4].strip()
1120 1120 lines = desc.splitlines()
1121 1121 i = 0
1122 1122 datastart = None
1123 1123 series = []
1124 1124 applied = []
1125 1125 qpp = None
1126 1126 for i in xrange(0, len(lines)):
1127 1127 if lines[i] == 'Patch Data:':
1128 1128 datastart = i + 1
1129 1129 elif lines[i].startswith('Dirstate:'):
1130 1130 l = lines[i].rstrip()
1131 1131 l = l[10:].split(' ')
1132 1132 qpp = [ hg.bin(x) for x in l ]
1133 1133 elif datastart != None:
1134 1134 l = lines[i].rstrip()
1135 1135 se = statusentry(l)
1136 1136 file_ = se.name
1137 1137 if se.rev:
1138 1138 applied.append(se)
1139 1139 series.append(file_)
1140 1140 if datastart == None:
1141 1141 self.ui.warn("No saved patch data found\n")
1142 1142 return 1
1143 1143 self.ui.warn("restoring status: %s\n" % lines[0])
1144 1144 self.full_series = series
1145 1145 self.applied = applied
1146 1146 self.parse_series()
1147 1147 self.series_dirty = 1
1148 1148 self.applied_dirty = 1
1149 1149 heads = repo.changelog.heads()
1150 1150 if delete:
1151 1151 if rev not in heads:
1152 1152 self.ui.warn("save entry has children, leaving it alone\n")
1153 1153 else:
1154 1154 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1155 1155 pp = repo.dirstate.parents()
1156 1156 if rev in pp:
1157 1157 update = True
1158 1158 else:
1159 1159 update = False
1160 1160 self.strip(repo, rev, update=update, backup='strip')
1161 1161 if qpp:
1162 1162 self.ui.warn("saved queue repository parents: %s %s\n" %
1163 1163 (hg.short(qpp[0]), hg.short(qpp[1])))
1164 1164 if qupdate:
1165 1165 print "queue directory updating"
1166 1166 r = self.qrepo()
1167 1167 if not r:
1168 1168 self.ui.warn("Unable to load queue repository\n")
1169 1169 return 1
1170 1170 hg.clean(r, qpp[0])
1171 1171
1172 1172 def save(self, repo, msg=None):
1173 1173 if len(self.applied) == 0:
1174 1174 self.ui.warn("save: no patches applied, exiting\n")
1175 1175 return 1
1176 1176 if self.issaveline(self.applied[-1]):
1177 1177 self.ui.warn("status is already saved\n")
1178 1178 return 1
1179 1179
1180 1180 ar = [ ':' + x for x in self.full_series ]
1181 1181 if not msg:
1182 1182 msg = "hg patches saved state"
1183 1183 else:
1184 1184 msg = "hg patches: " + msg.rstrip('\r\n')
1185 1185 r = self.qrepo()
1186 1186 if r:
1187 1187 pp = r.dirstate.parents()
1188 1188 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1189 1189 msg += "\n\nPatch Data:\n"
1190 1190 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1191 1191 "\n".join(ar) + '\n' or "")
1192 1192 n = repo.commit(None, text, user=None, force=1)
1193 1193 if not n:
1194 1194 self.ui.warn("repo commit failed\n")
1195 1195 return 1
1196 1196 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1197 1197 self.applied_dirty = 1
1198 1198
1199 1199 def full_series_end(self):
1200 1200 if len(self.applied) > 0:
1201 1201 p = self.applied[-1].name
1202 1202 end = self.find_series(p)
1203 1203 if end == None:
1204 1204 return len(self.full_series)
1205 1205 return end + 1
1206 1206 return 0
1207 1207
1208 1208 def series_end(self, all_patches=False):
1209 1209 end = 0
1210 1210 def next(start):
1211 1211 if all_patches:
1212 1212 return start
1213 1213 i = start
1214 1214 while i < len(self.series):
1215 1215 p, reason = self.pushable(i)
1216 1216 if p:
1217 1217 break
1218 1218 self.explain_pushable(i)
1219 1219 i += 1
1220 1220 return i
1221 1221 if len(self.applied) > 0:
1222 1222 p = self.applied[-1].name
1223 1223 try:
1224 1224 end = self.series.index(p)
1225 1225 except ValueError:
1226 1226 return 0
1227 1227 return next(end + 1)
1228 1228 return next(end)
1229 1229
1230 1230 def qapplied(self, repo, patch=None):
1231 1231 if patch and patch not in self.series:
1232 1232 raise util.Abort(_("patch %s is not in series file") % patch)
1233 1233 if not patch:
1234 1234 end = len(self.applied)
1235 1235 else:
1236 1236 end = self.series.index(patch) + 1
1237 1237 for x in xrange(end):
1238 1238 p = self.appliedname(x)
1239 1239 self.ui.write("%s\n" % p)
1240 1240
1241 1241 def appliedname(self, index):
1242 1242 pname = self.applied[index].name
1243 1243 if not self.ui.verbose:
1244 1244 p = pname
1245 1245 else:
1246 1246 p = str(self.series.index(pname)) + " " + pname
1247 1247 return p
1248 1248
1249 1249 def top(self, repo):
1250 1250 if len(self.applied):
1251 1251 p = self.appliedname(-1)
1252 1252 self.ui.write(p + '\n')
1253 1253 else:
1254 1254 self.ui.write("No patches applied\n")
1255 1255 return 1
1256 1256
1257 1257 def next(self, repo):
1258 1258 end = self.series_end()
1259 1259 if end == len(self.series):
1260 1260 self.ui.write("All patches applied\n")
1261 1261 return 1
1262 1262 else:
1263 1263 p = self.series[end]
1264 1264 if self.ui.verbose:
1265 1265 self.ui.write("%d " % self.series.index(p))
1266 1266 self.ui.write(p + '\n')
1267 1267
1268 1268 def prev(self, repo):
1269 1269 if len(self.applied) > 1:
1270 1270 p = self.appliedname(-2)
1271 1271 self.ui.write(p + '\n')
1272 1272 elif len(self.applied) == 1:
1273 1273 self.ui.write("Only one patch applied\n")
1274 1274 return 1
1275 1275 else:
1276 1276 self.ui.write("No patches applied\n")
1277 1277 return 1
1278 1278
1279 def qimport(self, repo, files, patch=None, existing=None, force=None):
1280 if len(files) > 1 and patch:
1279 def qimport(self, repo, files, patchname=None, existing=None, force=None):
1280 if len(files) > 1 and patchname:
1281 1281 raise util.Abort(_('option "-n" not valid when importing multiple '
1282 1282 'files'))
1283 1283 i = 0
1284 1284 added = []
1285 1285 for filename in files:
1286 1286 if existing:
1287 if not patch:
1288 patch = filename
1289 if not os.path.isfile(self.join(patch)):
1290 raise util.Abort(_("patch %s does not exist") % patch)
1287 if not patchname:
1288 patchname = filename
1289 if not os.path.isfile(self.join(patchname)):
1290 raise util.Abort(_("patch %s does not exist") % patchname)
1291 1291 else:
1292 1292 try:
1293 1293 text = file(filename).read()
1294 1294 except IOError:
1295 raise util.Abort(_("unable to read %s") % patch)
1296 if not patch:
1297 patch = os.path.split(filename)[1]
1298 if not force and os.path.exists(self.join(patch)):
1299 raise util.Abort(_('patch "%s" already exists') % patch)
1300 patchf = self.opener(patch, "w")
1295 raise util.Abort(_("unable to read %s") % patchname)
1296 if not patchname:
1297 patchname = os.path.basename(filename)
1298 if not force and os.path.exists(self.join(patchname)):
1299 raise util.Abort(_('patch "%s" already exists')
1300 % patchname)
1301 patchf = self.opener(patchname, "w")
1301 1302 patchf.write(text)
1302 if patch in self.series:
1303 if patchname in self.series:
1303 1304 raise util.Abort(_('patch %s is already in the series file')
1304 % patch)
1305 % patchname)
1305 1306 index = self.full_series_end() + i
1306 self.full_series[index:index] = [patch]
1307 self.full_series[index:index] = [patchname]
1307 1308 self.parse_series()
1308 self.ui.warn("adding %s to series file\n" % patch)
1309 self.ui.warn("adding %s to series file\n" % patchname)
1309 1310 i += 1
1310 added.append(patch)
1311 patch = None
1311 added.append(patchname)
1312 patchname = None
1312 1313 self.series_dirty = 1
1313 1314 qrepo = self.qrepo()
1314 1315 if qrepo:
1315 1316 qrepo.add(added)
1316 1317
1317 1318 def delete(ui, repo, patch, *patches, **opts):
1318 1319 """remove patches from queue
1319 1320
1320 1321 With --forget, mq will stop managing the named patches. The
1321 1322 patches must be applied and at the base of the stack. This option
1322 1323 is useful when the patches have been applied upstream.
1323 1324
1324 1325 Otherwise, the patches must not be applied.
1325 1326
1326 1327 With --keep, the patch files are preserved in the patch directory."""
1327 1328 q = repo.mq
1328 1329 q.delete(repo, (patch,) + patches, opts)
1329 1330 q.save_dirty()
1330 1331 return 0
1331 1332
1332 1333 def applied(ui, repo, patch=None, **opts):
1333 1334 """print the patches already applied"""
1334 1335 repo.mq.qapplied(repo, patch)
1335 1336 return 0
1336 1337
1337 1338 def unapplied(ui, repo, patch=None, **opts):
1338 1339 """print the patches not yet applied"""
1339 1340 for i, p in repo.mq.unapplied(repo, patch):
1340 1341 if ui.verbose:
1341 1342 ui.write("%d " % i)
1342 1343 ui.write("%s\n" % p)
1343 1344
1344 1345 def qimport(ui, repo, *filename, **opts):
1345 1346 """import a patch"""
1346 1347 q = repo.mq
1347 q.qimport(repo, filename, patch=opts['name'],
1348 q.qimport(repo, filename, patchname=opts['name'],
1348 1349 existing=opts['existing'], force=opts['force'])
1349 1350 q.save_dirty()
1350 1351 return 0
1351 1352
1352 1353 def init(ui, repo, **opts):
1353 1354 """init a new queue repository
1354 1355
1355 1356 The queue repository is unversioned by default. If -c is
1356 1357 specified, qinit will create a separate nested repository
1357 1358 for patches. Use qcommit to commit changes to this queue
1358 1359 repository."""
1359 1360 q = repo.mq
1360 1361 r = q.init(repo, create=opts['create_repo'])
1361 1362 q.save_dirty()
1362 1363 if r:
1363 1364 fp = r.wopener('.hgignore', 'w')
1364 1365 print >> fp, 'syntax: glob'
1365 1366 print >> fp, 'status'
1366 1367 fp.close()
1367 1368 r.wopener('series', 'w').close()
1368 1369 r.add(['.hgignore', 'series'])
1369 1370 return 0
1370 1371
1371 1372 def clone(ui, source, dest=None, **opts):
1372 1373 '''clone main and patch repository at same time
1373 1374
1374 1375 If source is local, destination will have no patches applied. If
1375 1376 source is remote, this command can not check if patches are
1376 1377 applied in source, so cannot guarantee that patches are not
1377 1378 applied in destination. If you clone remote repository, be sure
1378 1379 before that it has no patches applied.
1379 1380
1380 1381 Source patch repository is looked for in <src>/.hg/patches by
1381 1382 default. Use -p <url> to change.
1382 1383 '''
1383 1384 commands.setremoteconfig(ui, opts)
1384 1385 if dest is None:
1385 1386 dest = hg.defaultdest(source)
1386 1387 sr = hg.repository(ui, ui.expandpath(source))
1387 1388 qbase, destrev = None, None
1388 1389 if sr.local():
1389 1390 reposetup(ui, sr)
1390 1391 if sr.mq.applied:
1391 1392 qbase = revlog.bin(sr.mq.applied[0].rev)
1392 1393 if not hg.islocal(dest):
1393 1394 destrev = sr.parents(qbase)[0]
1394 1395 ui.note(_('cloning main repo\n'))
1395 1396 sr, dr = hg.clone(ui, sr, dest,
1396 1397 pull=opts['pull'],
1397 1398 rev=destrev,
1398 1399 update=False,
1399 1400 stream=opts['uncompressed'])
1400 1401 ui.note(_('cloning patch repo\n'))
1401 1402 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1402 1403 dr.url() + '/.hg/patches',
1403 1404 pull=opts['pull'],
1404 1405 update=not opts['noupdate'],
1405 1406 stream=opts['uncompressed'])
1406 1407 if dr.local():
1407 1408 if qbase:
1408 1409 ui.note(_('stripping applied patches from destination repo\n'))
1409 1410 reposetup(ui, dr)
1410 1411 dr.mq.strip(dr, qbase, update=False, backup=None)
1411 1412 if not opts['noupdate']:
1412 1413 ui.note(_('updating destination repo\n'))
1413 1414 hg.update(dr, dr.changelog.tip())
1414 1415
1415 1416 def commit(ui, repo, *pats, **opts):
1416 1417 """commit changes in the queue repository"""
1417 1418 q = repo.mq
1418 1419 r = q.qrepo()
1419 1420 if not r: raise util.Abort('no queue repository')
1420 1421 commands.commit(r.ui, r, *pats, **opts)
1421 1422
1422 1423 def series(ui, repo, **opts):
1423 1424 """print the entire series file"""
1424 1425 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1425 1426 return 0
1426 1427
1427 1428 def top(ui, repo, **opts):
1428 1429 """print the name of the current patch"""
1429 1430 return repo.mq.top(repo)
1430 1431
1431 1432 def next(ui, repo, **opts):
1432 1433 """print the name of the next patch"""
1433 1434 return repo.mq.next(repo)
1434 1435
1435 1436 def prev(ui, repo, **opts):
1436 1437 """print the name of the previous patch"""
1437 1438 return repo.mq.prev(repo)
1438 1439
1439 1440 def new(ui, repo, patch, **opts):
1440 1441 """create a new patch
1441 1442
1442 1443 qnew creates a new patch on top of the currently-applied patch
1443 1444 (if any). It will refuse to run if there are any outstanding
1444 1445 changes unless -f is specified, in which case the patch will
1445 1446 be initialised with them.
1446 1447
1447 1448 -e, -m or -l set the patch header as well as the commit message.
1448 1449 If none is specified, the patch header is empty and the
1449 1450 commit message is 'New patch: PATCH'"""
1450 1451 q = repo.mq
1451 1452 message = commands.logmessage(opts)
1452 1453 if opts['edit']:
1453 1454 message = ui.edit(message, ui.username())
1454 1455 q.new(repo, patch, msg=message, force=opts['force'])
1455 1456 q.save_dirty()
1456 1457 return 0
1457 1458
1458 1459 def refresh(ui, repo, *pats, **opts):
1459 1460 """update the current patch
1460 1461
1461 1462 If any file patterns are provided, the refreshed patch will contain only
1462 1463 the modifications that match those patterns; the remaining modifications
1463 1464 will remain in the working directory.
1464 1465 """
1465 1466 q = repo.mq
1466 1467 message = commands.logmessage(opts)
1467 1468 if opts['edit']:
1468 1469 if message:
1469 1470 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1470 1471 patch = q.applied[-1].name
1471 1472 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1472 1473 message = ui.edit('\n'.join(message), user or ui.username())
1473 1474 ret = q.refresh(repo, pats, msg=message, **opts)
1474 1475 q.save_dirty()
1475 1476 return ret
1476 1477
1477 1478 def diff(ui, repo, *pats, **opts):
1478 1479 """diff of the current patch"""
1479 1480 repo.mq.diff(repo, pats, opts)
1480 1481 return 0
1481 1482
1482 1483 def fold(ui, repo, *files, **opts):
1483 1484 """fold the named patches into the current patch
1484 1485
1485 1486 Patches must not yet be applied. Each patch will be successively
1486 1487 applied to the current patch in the order given. If all the
1487 1488 patches apply successfully, the current patch will be refreshed
1488 1489 with the new cumulative patch, and the folded patches will
1489 1490 be deleted. With -k/--keep, the folded patch files will not
1490 1491 be removed afterwards.
1491 1492
1492 1493 The header for each folded patch will be concatenated with
1493 1494 the current patch header, separated by a line of '* * *'."""
1494 1495
1495 1496 q = repo.mq
1496 1497
1497 1498 if not files:
1498 1499 raise util.Abort(_('qfold requires at least one patch name'))
1499 1500 if not q.check_toppatch(repo):
1500 1501 raise util.Abort(_('No patches applied'))
1501 1502
1502 1503 message = commands.logmessage(opts)
1503 1504 if opts['edit']:
1504 1505 if message:
1505 1506 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1506 1507
1507 1508 parent = q.lookup('qtip')
1508 1509 patches = []
1509 1510 messages = []
1510 1511 for f in files:
1511 1512 p = q.lookup(f)
1512 1513 if p in patches or p == parent:
1513 1514 ui.warn(_('Skipping already folded patch %s') % p)
1514 1515 if q.isapplied(p):
1515 1516 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1516 1517 patches.append(p)
1517 1518
1518 1519 for p in patches:
1519 1520 if not message:
1520 1521 messages.append(q.readheaders(p)[0])
1521 1522 pf = q.join(p)
1522 1523 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1523 1524 if not patchsuccess:
1524 1525 raise util.Abort(_('Error folding patch %s') % p)
1525 1526 patch.updatedir(ui, repo, files)
1526 1527
1527 1528 if not message:
1528 1529 message, comments, user = q.readheaders(parent)[0:3]
1529 1530 for msg in messages:
1530 1531 message.append('* * *')
1531 1532 message.extend(msg)
1532 1533 message = '\n'.join(message)
1533 1534
1534 1535 if opts['edit']:
1535 1536 message = ui.edit(message, user or ui.username())
1536 1537
1537 1538 q.refresh(repo, msg=message)
1538 1539 q.delete(repo, patches, keep=opts['keep'])
1539 1540 q.save_dirty()
1540 1541
1541 1542 def guard(ui, repo, *args, **opts):
1542 1543 '''set or print guards for a patch
1543 1544
1544 1545 Guards control whether a patch can be pushed. A patch with no
1545 1546 guards is always pushed. A patch with a positive guard ("+foo") is
1546 1547 pushed only if the qselect command has activated it. A patch with
1547 1548 a negative guard ("-foo") is never pushed if the qselect command
1548 1549 has activated it.
1549 1550
1550 1551 With no arguments, print the currently active guards.
1551 1552 With arguments, set guards for the named patch.
1552 1553
1553 1554 To set a negative guard "-foo" on topmost patch ("--" is needed so
1554 1555 hg will not interpret "-foo" as an option):
1555 1556 hg qguard -- -foo
1556 1557
1557 1558 To set guards on another patch:
1558 1559 hg qguard other.patch +2.6.17 -stable
1559 1560 '''
1560 1561 def status(idx):
1561 1562 guards = q.series_guards[idx] or ['unguarded']
1562 1563 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1563 1564 q = repo.mq
1564 1565 patch = None
1565 1566 args = list(args)
1566 1567 if opts['list']:
1567 1568 if args or opts['none']:
1568 1569 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1569 1570 for i in xrange(len(q.series)):
1570 1571 status(i)
1571 1572 return
1572 1573 if not args or args[0][0:1] in '-+':
1573 1574 if not q.applied:
1574 1575 raise util.Abort(_('no patches applied'))
1575 1576 patch = q.applied[-1].name
1576 1577 if patch is None and args[0][0:1] not in '-+':
1577 1578 patch = args.pop(0)
1578 1579 if patch is None:
1579 1580 raise util.Abort(_('no patch to work with'))
1580 1581 if args or opts['none']:
1581 1582 q.set_guards(q.find_series(patch), args)
1582 1583 q.save_dirty()
1583 1584 else:
1584 1585 status(q.series.index(q.lookup(patch)))
1585 1586
1586 1587 def header(ui, repo, patch=None):
1587 1588 """Print the header of the topmost or specified patch"""
1588 1589 q = repo.mq
1589 1590
1590 1591 if patch:
1591 1592 patch = q.lookup(patch)
1592 1593 else:
1593 1594 if not q.applied:
1594 1595 ui.write('No patches applied\n')
1595 1596 return 1
1596 1597 patch = q.lookup('qtip')
1597 1598 message = repo.mq.readheaders(patch)[0]
1598 1599
1599 1600 ui.write('\n'.join(message) + '\n')
1600 1601
1601 1602 def lastsavename(path):
1602 1603 (directory, base) = os.path.split(path)
1603 1604 names = os.listdir(directory)
1604 1605 namere = re.compile("%s.([0-9]+)" % base)
1605 1606 maxindex = None
1606 1607 maxname = None
1607 1608 for f in names:
1608 1609 m = namere.match(f)
1609 1610 if m:
1610 1611 index = int(m.group(1))
1611 1612 if maxindex == None or index > maxindex:
1612 1613 maxindex = index
1613 1614 maxname = f
1614 1615 if maxname:
1615 1616 return (os.path.join(directory, maxname), maxindex)
1616 1617 return (None, None)
1617 1618
1618 1619 def savename(path):
1619 1620 (last, index) = lastsavename(path)
1620 1621 if last is None:
1621 1622 index = 0
1622 1623 newpath = path + ".%d" % (index + 1)
1623 1624 return newpath
1624 1625
1625 1626 def push(ui, repo, patch=None, **opts):
1626 1627 """push the next patch onto the stack"""
1627 1628 q = repo.mq
1628 1629 mergeq = None
1629 1630
1630 1631 if opts['all']:
1631 1632 patch = q.series[-1]
1632 1633 if opts['merge']:
1633 1634 if opts['name']:
1634 1635 newpath = opts['name']
1635 1636 else:
1636 1637 newpath, i = lastsavename(q.path)
1637 1638 if not newpath:
1638 1639 ui.warn("no saved queues found, please use -n\n")
1639 1640 return 1
1640 1641 mergeq = queue(ui, repo.join(""), newpath)
1641 1642 ui.warn("merging with queue at: %s\n" % mergeq.path)
1642 1643 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1643 1644 mergeq=mergeq)
1644 1645 q.save_dirty()
1645 1646 return ret
1646 1647
1647 1648 def pop(ui, repo, patch=None, **opts):
1648 1649 """pop the current patch off the stack"""
1649 1650 localupdate = True
1650 1651 if opts['name']:
1651 1652 q = queue(ui, repo.join(""), repo.join(opts['name']))
1652 1653 ui.warn('using patch queue: %s\n' % q.path)
1653 1654 localupdate = False
1654 1655 else:
1655 1656 q = repo.mq
1656 1657 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1657 1658 q.save_dirty()
1658 1659 return 0
1659 1660
1660 1661 def rename(ui, repo, patch, name=None, **opts):
1661 1662 """rename a patch
1662 1663
1663 1664 With one argument, renames the current patch to PATCH1.
1664 1665 With two arguments, renames PATCH1 to PATCH2."""
1665 1666
1666 1667 q = repo.mq
1667 1668
1668 1669 if not name:
1669 1670 name = patch
1670 1671 patch = None
1671 1672
1672 1673 if patch:
1673 1674 patch = q.lookup(patch)
1674 1675 else:
1675 1676 if not q.applied:
1676 1677 ui.write(_('No patches applied\n'))
1677 1678 return
1678 1679 patch = q.lookup('qtip')
1679 1680 absdest = q.join(name)
1680 1681 if os.path.isdir(absdest):
1681 1682 name = os.path.join(name, os.path.basename(patch))
1682 1683 absdest = q.join(name)
1683 1684 if os.path.exists(absdest):
1684 1685 raise util.Abort(_('%s already exists') % absdest)
1685 1686
1686 1687 if name in q.series:
1687 1688 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1688 1689
1689 1690 if ui.verbose:
1690 1691 ui.write('Renaming %s to %s\n' % (patch, name))
1691 1692 i = q.find_series(patch)
1692 1693 q.full_series[i] = name
1693 1694 q.parse_series()
1694 1695 q.series_dirty = 1
1695 1696
1696 1697 info = q.isapplied(patch)
1697 1698 if info:
1698 1699 q.applied[info[0]] = statusentry(info[1], name)
1699 1700 q.applied_dirty = 1
1700 1701
1701 1702 util.rename(q.join(patch), absdest)
1702 1703 r = q.qrepo()
1703 1704 if r:
1704 1705 wlock = r.wlock()
1705 1706 if r.dirstate.state(name) == 'r':
1706 1707 r.undelete([name], wlock)
1707 1708 r.copy(patch, name, wlock)
1708 1709 r.remove([patch], False, wlock)
1709 1710
1710 1711 q.save_dirty()
1711 1712
1712 1713 def restore(ui, repo, rev, **opts):
1713 1714 """restore the queue state saved by a rev"""
1714 1715 rev = repo.lookup(rev)
1715 1716 q = repo.mq
1716 1717 q.restore(repo, rev, delete=opts['delete'],
1717 1718 qupdate=opts['update'])
1718 1719 q.save_dirty()
1719 1720 return 0
1720 1721
1721 1722 def save(ui, repo, **opts):
1722 1723 """save current queue state"""
1723 1724 q = repo.mq
1724 1725 message = commands.logmessage(opts)
1725 1726 ret = q.save(repo, msg=message)
1726 1727 if ret:
1727 1728 return ret
1728 1729 q.save_dirty()
1729 1730 if opts['copy']:
1730 1731 path = q.path
1731 1732 if opts['name']:
1732 1733 newpath = os.path.join(q.basepath, opts['name'])
1733 1734 if os.path.exists(newpath):
1734 1735 if not os.path.isdir(newpath):
1735 1736 raise util.Abort(_('destination %s exists and is not '
1736 1737 'a directory') % newpath)
1737 1738 if not opts['force']:
1738 1739 raise util.Abort(_('destination %s exists, '
1739 1740 'use -f to force') % newpath)
1740 1741 else:
1741 1742 newpath = savename(path)
1742 1743 ui.warn("copy %s to %s\n" % (path, newpath))
1743 1744 util.copyfiles(path, newpath)
1744 1745 if opts['empty']:
1745 1746 try:
1746 1747 os.unlink(q.join(q.status_path))
1747 1748 except:
1748 1749 pass
1749 1750 return 0
1750 1751
1751 1752 def strip(ui, repo, rev, **opts):
1752 1753 """strip a revision and all later revs on the same branch"""
1753 1754 rev = repo.lookup(rev)
1754 1755 backup = 'all'
1755 1756 if opts['backup']:
1756 1757 backup = 'strip'
1757 1758 elif opts['nobackup']:
1758 1759 backup = 'none'
1759 1760 update = repo.dirstate.parents()[0] != revlog.nullid
1760 1761 repo.mq.strip(repo, rev, backup=backup, update=update)
1761 1762 return 0
1762 1763
1763 1764 def select(ui, repo, *args, **opts):
1764 1765 '''set or print guarded patches to push
1765 1766
1766 1767 Use the qguard command to set or print guards on patch, then use
1767 1768 qselect to tell mq which guards to use. A patch will be pushed if it
1768 1769 has no guards or any positive guards match the currently selected guard,
1769 1770 but will not be pushed if any negative guards match the current guard.
1770 1771 For example:
1771 1772
1772 1773 qguard foo.patch -stable (negative guard)
1773 1774 qguard bar.patch +stable (positive guard)
1774 1775 qselect stable
1775 1776
1776 1777 This activates the "stable" guard. mq will skip foo.patch (because
1777 1778 it has a negative match) but push bar.patch (because it
1778 1779 has a positive match).
1779 1780
1780 1781 With no arguments, prints the currently active guards.
1781 1782 With one argument, sets the active guard.
1782 1783
1783 1784 Use -n/--none to deactivate guards (no other arguments needed).
1784 1785 When no guards are active, patches with positive guards are skipped
1785 1786 and patches with negative guards are pushed.
1786 1787
1787 1788 qselect can change the guards on applied patches. It does not pop
1788 1789 guarded patches by default. Use --pop to pop back to the last applied
1789 1790 patch that is not guarded. Use --reapply (which implies --pop) to push
1790 1791 back to the current patch afterwards, but skip guarded patches.
1791 1792
1792 1793 Use -s/--series to print a list of all guards in the series file (no
1793 1794 other arguments needed). Use -v for more information.'''
1794 1795
1795 1796 q = repo.mq
1796 1797 guards = q.active()
1797 1798 if args or opts['none']:
1798 1799 old_unapplied = q.unapplied(repo)
1799 1800 old_guarded = [i for i in xrange(len(q.applied)) if
1800 1801 not q.pushable(i)[0]]
1801 1802 q.set_active(args)
1802 1803 q.save_dirty()
1803 1804 if not args:
1804 1805 ui.status(_('guards deactivated\n'))
1805 1806 if not opts['pop'] and not opts['reapply']:
1806 1807 unapplied = q.unapplied(repo)
1807 1808 guarded = [i for i in xrange(len(q.applied))
1808 1809 if not q.pushable(i)[0]]
1809 1810 if len(unapplied) != len(old_unapplied):
1810 1811 ui.status(_('number of unguarded, unapplied patches has '
1811 1812 'changed from %d to %d\n') %
1812 1813 (len(old_unapplied), len(unapplied)))
1813 1814 if len(guarded) != len(old_guarded):
1814 1815 ui.status(_('number of guarded, applied patches has changed '
1815 1816 'from %d to %d\n') %
1816 1817 (len(old_guarded), len(guarded)))
1817 1818 elif opts['series']:
1818 1819 guards = {}
1819 1820 noguards = 0
1820 1821 for gs in q.series_guards:
1821 1822 if not gs:
1822 1823 noguards += 1
1823 1824 for g in gs:
1824 1825 guards.setdefault(g, 0)
1825 1826 guards[g] += 1
1826 1827 if ui.verbose:
1827 1828 guards['NONE'] = noguards
1828 1829 guards = guards.items()
1829 1830 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1830 1831 if guards:
1831 1832 ui.note(_('guards in series file:\n'))
1832 1833 for guard, count in guards:
1833 1834 ui.note('%2d ' % count)
1834 1835 ui.write(guard, '\n')
1835 1836 else:
1836 1837 ui.note(_('no guards in series file\n'))
1837 1838 else:
1838 1839 if guards:
1839 1840 ui.note(_('active guards:\n'))
1840 1841 for g in guards:
1841 1842 ui.write(g, '\n')
1842 1843 else:
1843 1844 ui.write(_('no active guards\n'))
1844 1845 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1845 1846 popped = False
1846 1847 if opts['pop'] or opts['reapply']:
1847 1848 for i in xrange(len(q.applied)):
1848 1849 pushable, reason = q.pushable(i)
1849 1850 if not pushable:
1850 1851 ui.status(_('popping guarded patches\n'))
1851 1852 popped = True
1852 1853 if i == 0:
1853 1854 q.pop(repo, all=True)
1854 1855 else:
1855 1856 q.pop(repo, i-1)
1856 1857 break
1857 1858 if popped:
1858 1859 try:
1859 1860 if reapply:
1860 1861 ui.status(_('reapplying unguarded patches\n'))
1861 1862 q.push(repo, reapply)
1862 1863 finally:
1863 1864 q.save_dirty()
1864 1865
1865 1866 def reposetup(ui, repo):
1866 1867 class mqrepo(repo.__class__):
1867 1868 def abort_if_wdir_patched(self, errmsg, force=False):
1868 1869 if self.mq.applied and not force:
1869 1870 parent = revlog.hex(self.dirstate.parents()[0])
1870 1871 if parent in [s.rev for s in self.mq.applied]:
1871 1872 raise util.Abort(errmsg)
1872 1873
1873 1874 def commit(self, *args, **opts):
1874 1875 if len(args) >= 6:
1875 1876 force = args[5]
1876 1877 else:
1877 1878 force = opts.get('force')
1878 1879 self.abort_if_wdir_patched(
1879 1880 _('cannot commit over an applied mq patch'),
1880 1881 force)
1881 1882
1882 1883 return super(mqrepo, self).commit(*args, **opts)
1883 1884
1884 1885 def push(self, remote, force=False, revs=None):
1885 1886 if self.mq.applied and not force:
1886 1887 raise util.Abort(_('source has mq patches applied'))
1887 1888 return super(mqrepo, self).push(remote, force, revs)
1888 1889
1889 1890 def tags(self):
1890 1891 if self.tagscache:
1891 1892 return self.tagscache
1892 1893
1893 1894 tagscache = super(mqrepo, self).tags()
1894 1895
1895 1896 q = self.mq
1896 1897 if not q.applied:
1897 1898 return tagscache
1898 1899
1899 1900 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1900 1901 mqtags.append((mqtags[-1][0], 'qtip'))
1901 1902 mqtags.append((mqtags[0][0], 'qbase'))
1902 1903 for patch in mqtags:
1903 1904 if patch[1] in tagscache:
1904 1905 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1905 1906 else:
1906 1907 tagscache[patch[1]] = revlog.bin(patch[0])
1907 1908
1908 1909 return tagscache
1909 1910
1910 1911 if repo.local():
1911 1912 repo.__class__ = mqrepo
1912 1913 repo.mq = queue(ui, repo.join(""))
1913 1914
1914 1915 cmdtable = {
1915 1916 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1916 1917 "qclone": (clone,
1917 1918 [('', 'pull', None, _('use pull protocol to copy metadata')),
1918 1919 ('U', 'noupdate', None, _('do not update the new working directories')),
1919 1920 ('', 'uncompressed', None,
1920 1921 _('use uncompressed transfer (fast over LAN)')),
1921 1922 ('e', 'ssh', '', _('specify ssh command to use')),
1922 1923 ('p', 'patches', '', _('location of source patch repo')),
1923 1924 ('', 'remotecmd', '',
1924 1925 _('specify hg command to run on the remote side'))],
1925 1926 'hg qclone [OPTION]... SOURCE [DEST]'),
1926 1927 "qcommit|qci":
1927 1928 (commit,
1928 1929 commands.table["^commit|ci"][1],
1929 1930 'hg qcommit [OPTION]... [FILE]...'),
1930 1931 "^qdiff": (diff,
1931 1932 [('I', 'include', [], _('include names matching the given patterns')),
1932 1933 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1933 1934 'hg qdiff [-I] [-X] [FILE]...'),
1934 1935 "qdelete|qremove|qrm":
1935 1936 (delete,
1936 1937 [('f', 'forget', None, _('stop managing an applied patch')),
1937 1938 ('k', 'keep', None, _('keep patch file'))],
1938 1939 'hg qdelete [-f] [-k] PATCH'),
1939 1940 'qfold':
1940 1941 (fold,
1941 1942 [('e', 'edit', None, _('edit patch header')),
1942 1943 ('k', 'keep', None, _('keep folded patch files')),
1943 1944 ('m', 'message', '', _('set patch header to <text>')),
1944 1945 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1945 1946 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1946 1947 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1947 1948 ('n', 'none', None, _('drop all guards'))],
1948 1949 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1949 1950 'qheader': (header, [],
1950 1951 _('hg qheader [PATCH]')),
1951 1952 "^qimport":
1952 1953 (qimport,
1953 1954 [('e', 'existing', None, 'import file in patch dir'),
1954 1955 ('n', 'name', '', 'patch file name'),
1955 1956 ('f', 'force', None, 'overwrite existing files')],
1956 1957 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1957 1958 "^qinit":
1958 1959 (init,
1959 1960 [('c', 'create-repo', None, 'create queue repository')],
1960 1961 'hg qinit [-c]'),
1961 1962 "qnew":
1962 1963 (new,
1963 1964 [('e', 'edit', None, _('edit commit message')),
1964 1965 ('m', 'message', '', _('use <text> as commit message')),
1965 1966 ('l', 'logfile', '', _('read the commit message from <file>')),
1966 1967 ('f', 'force', None, _('import uncommitted changes into patch'))],
1967 1968 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
1968 1969 "qnext": (next, [], 'hg qnext'),
1969 1970 "qprev": (prev, [], 'hg qprev'),
1970 1971 "^qpop":
1971 1972 (pop,
1972 1973 [('a', 'all', None, 'pop all patches'),
1973 1974 ('n', 'name', '', 'queue name to pop'),
1974 1975 ('f', 'force', None, 'forget any local changes')],
1975 1976 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1976 1977 "^qpush":
1977 1978 (push,
1978 1979 [('f', 'force', None, 'apply if the patch has rejects'),
1979 1980 ('l', 'list', None, 'list patch name in commit text'),
1980 1981 ('a', 'all', None, 'apply all patches'),
1981 1982 ('m', 'merge', None, 'merge from another queue'),
1982 1983 ('n', 'name', '', 'merge queue name')],
1983 1984 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1984 1985 "^qrefresh":
1985 1986 (refresh,
1986 1987 [('e', 'edit', None, _('edit commit message')),
1987 1988 ('m', 'message', '', _('change commit message with <text>')),
1988 1989 ('l', 'logfile', '', _('change commit message with <file> content')),
1989 1990 ('g', 'git', None, _('use git extended diff format')),
1990 1991 ('s', 'short', None, 'short refresh'),
1991 1992 ('I', 'include', [], _('include names matching the given patterns')),
1992 1993 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1993 1994 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
1994 1995 'qrename|qmv':
1995 1996 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1996 1997 "qrestore":
1997 1998 (restore,
1998 1999 [('d', 'delete', None, 'delete save entry'),
1999 2000 ('u', 'update', None, 'update queue working dir')],
2000 2001 'hg qrestore [-d] [-u] REV'),
2001 2002 "qsave":
2002 2003 (save,
2003 2004 [('m', 'message', '', _('use <text> as commit message')),
2004 2005 ('l', 'logfile', '', _('read the commit message from <file>')),
2005 2006 ('c', 'copy', None, 'copy patch directory'),
2006 2007 ('n', 'name', '', 'copy directory name'),
2007 2008 ('e', 'empty', None, 'clear queue status file'),
2008 2009 ('f', 'force', None, 'force copy')],
2009 2010 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2010 2011 "qselect": (select,
2011 2012 [('n', 'none', None, _('disable all guards')),
2012 2013 ('s', 'series', None, _('list all guards in series file')),
2013 2014 ('', 'pop', None,
2014 2015 _('pop to before first guarded applied patch')),
2015 2016 ('', 'reapply', None, _('pop, then reapply patches'))],
2016 2017 'hg qselect [OPTION...] [GUARD...]'),
2017 2018 "qseries":
2018 2019 (series,
2019 2020 [('m', 'missing', None, 'print patches not in series'),
2020 2021 ('s', 'summary', None, _('print first line of patch header'))],
2021 2022 'hg qseries [-ms]'),
2022 2023 "^strip":
2023 2024 (strip,
2024 2025 [('f', 'force', None, 'force multi-head removal'),
2025 2026 ('b', 'backup', None, 'bundle unrelated changesets'),
2026 2027 ('n', 'nobackup', None, 'no backups')],
2027 2028 'hg strip [-f] [-b] [-n] REV'),
2028 2029 "qtop": (top, [], 'hg qtop'),
2029 2030 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
2030 2031 }
General Comments 0
You need to be logged in to leave comments. Login now