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