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