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