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