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