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