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