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