##// END OF EJS Templates
merge with stable
Matt Mackall -
r16279:531e69ff merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,3403 +1,3404
1 1 # mq.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''manage a stack of patches
9 9
10 10 This extension lets you work with a stack of patches in a Mercurial
11 11 repository. It manages two stacks of patches - all known patches, and
12 12 applied patches (subset of known patches).
13 13
14 14 Known patches are represented as patch files in the .hg/patches
15 15 directory. Applied patches are both patch files and changesets.
16 16
17 17 Common tasks (use :hg:`help command` for more details)::
18 18
19 19 create new patch qnew
20 20 import existing patch qimport
21 21
22 22 print patch series qseries
23 23 print applied patches qapplied
24 24
25 25 add known patch to applied stack qpush
26 26 remove patch from applied stack qpop
27 27 refresh contents of top applied patch qrefresh
28 28
29 29 By default, mq will automatically use git patches when required to
30 30 avoid losing file mode changes, copy records, binary files or empty
31 31 files creations or deletions. This behaviour can be configured with::
32 32
33 33 [mq]
34 34 git = auto/keep/yes/no
35 35
36 36 If set to 'keep', mq will obey the [diff] section configuration while
37 37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 38 'no', mq will override the [diff] section and always generate git or
39 39 regular patches, possibly losing data in the second case.
40 40
41 41 It may be desirable for mq changesets to be kept in the secret phase (see
42 42 :hg:`help phases`), which can be enabled with the following setting::
43 43
44 44 [mq]
45 45 secret = True
46 46
47 47 You will by default be managing a patch queue named "patches". You can
48 48 create other, independent patch queues with the :hg:`qqueue` command.
49 49 '''
50 50
51 51 from mercurial.i18n import _
52 52 from mercurial.node import bin, hex, short, nullid, nullrev
53 53 from mercurial.lock import release
54 54 from mercurial import commands, cmdutil, hg, scmutil, util, revset
55 55 from mercurial import repair, extensions, url, error, phases
56 56 from mercurial import patch as patchmod
57 57 import os, re, errno, shutil
58 58
59 59 commands.norepo += " qclone"
60 60
61 61 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
62 62
63 63 cmdtable = {}
64 64 command = cmdutil.command(cmdtable)
65 65
66 66 # Patch names looks like unix-file names.
67 67 # They must be joinable with queue directory and result in the patch path.
68 68 normname = util.normpath
69 69
70 70 class statusentry(object):
71 71 def __init__(self, node, name):
72 72 self.node, self.name = node, name
73 73 def __repr__(self):
74 74 return hex(self.node) + ':' + self.name
75 75
76 76 class patchheader(object):
77 77 def __init__(self, pf, plainmode=False):
78 78 def eatdiff(lines):
79 79 while lines:
80 80 l = lines[-1]
81 81 if (l.startswith("diff -") or
82 82 l.startswith("Index:") or
83 83 l.startswith("===========")):
84 84 del lines[-1]
85 85 else:
86 86 break
87 87 def eatempty(lines):
88 88 while lines:
89 89 if not lines[-1].strip():
90 90 del lines[-1]
91 91 else:
92 92 break
93 93
94 94 message = []
95 95 comments = []
96 96 user = None
97 97 date = None
98 98 parent = None
99 99 format = None
100 100 subject = None
101 101 branch = None
102 102 nodeid = None
103 103 diffstart = 0
104 104
105 105 for line in file(pf):
106 106 line = line.rstrip()
107 107 if (line.startswith('diff --git')
108 108 or (diffstart and line.startswith('+++ '))):
109 109 diffstart = 2
110 110 break
111 111 diffstart = 0 # reset
112 112 if line.startswith("--- "):
113 113 diffstart = 1
114 114 continue
115 115 elif format == "hgpatch":
116 116 # parse values when importing the result of an hg export
117 117 if line.startswith("# User "):
118 118 user = line[7:]
119 119 elif line.startswith("# Date "):
120 120 date = line[7:]
121 121 elif line.startswith("# Parent "):
122 122 parent = line[9:].lstrip()
123 123 elif line.startswith("# Branch "):
124 124 branch = line[9:]
125 125 elif line.startswith("# Node ID "):
126 126 nodeid = line[10:]
127 127 elif not line.startswith("# ") and line:
128 128 message.append(line)
129 129 format = None
130 130 elif line == '# HG changeset patch':
131 131 message = []
132 132 format = "hgpatch"
133 133 elif (format != "tagdone" and (line.startswith("Subject: ") or
134 134 line.startswith("subject: "))):
135 135 subject = line[9:]
136 136 format = "tag"
137 137 elif (format != "tagdone" and (line.startswith("From: ") or
138 138 line.startswith("from: "))):
139 139 user = line[6:]
140 140 format = "tag"
141 141 elif (format != "tagdone" and (line.startswith("Date: ") or
142 142 line.startswith("date: "))):
143 143 date = line[6:]
144 144 format = "tag"
145 145 elif format == "tag" and line == "":
146 146 # when looking for tags (subject: from: etc) they
147 147 # end once you find a blank line in the source
148 148 format = "tagdone"
149 149 elif message or line:
150 150 message.append(line)
151 151 comments.append(line)
152 152
153 153 eatdiff(message)
154 154 eatdiff(comments)
155 155 # Remember the exact starting line of the patch diffs before consuming
156 156 # empty lines, for external use by TortoiseHg and others
157 157 self.diffstartline = len(comments)
158 158 eatempty(message)
159 159 eatempty(comments)
160 160
161 161 # make sure message isn't empty
162 162 if format and format.startswith("tag") and subject:
163 163 message.insert(0, "")
164 164 message.insert(0, subject)
165 165
166 166 self.message = message
167 167 self.comments = comments
168 168 self.user = user
169 169 self.date = date
170 170 self.parent = parent
171 171 # nodeid and branch are for external use by TortoiseHg and others
172 172 self.nodeid = nodeid
173 173 self.branch = branch
174 174 self.haspatch = diffstart > 1
175 175 self.plainmode = plainmode
176 176
177 177 def setuser(self, user):
178 178 if not self.updateheader(['From: ', '# User '], user):
179 179 try:
180 180 patchheaderat = self.comments.index('# HG changeset patch')
181 181 self.comments.insert(patchheaderat + 1, '# User ' + user)
182 182 except ValueError:
183 183 if self.plainmode or self._hasheader(['Date: ']):
184 184 self.comments = ['From: ' + user] + self.comments
185 185 else:
186 186 tmp = ['# HG changeset patch', '# User ' + user, '']
187 187 self.comments = tmp + self.comments
188 188 self.user = user
189 189
190 190 def setdate(self, date):
191 191 if not self.updateheader(['Date: ', '# Date '], date):
192 192 try:
193 193 patchheaderat = self.comments.index('# HG changeset patch')
194 194 self.comments.insert(patchheaderat + 1, '# Date ' + date)
195 195 except ValueError:
196 196 if self.plainmode or self._hasheader(['From: ']):
197 197 self.comments = ['Date: ' + date] + self.comments
198 198 else:
199 199 tmp = ['# HG changeset patch', '# Date ' + date, '']
200 200 self.comments = tmp + self.comments
201 201 self.date = date
202 202
203 203 def setparent(self, parent):
204 204 if not self.updateheader(['# Parent '], parent):
205 205 try:
206 206 patchheaderat = self.comments.index('# HG changeset patch')
207 207 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
208 208 except ValueError:
209 209 pass
210 210 self.parent = parent
211 211
212 212 def setmessage(self, message):
213 213 if self.comments:
214 214 self._delmsg()
215 215 self.message = [message]
216 216 self.comments += self.message
217 217
218 218 def updateheader(self, prefixes, new):
219 219 '''Update all references to a field in the patch header.
220 220 Return whether the field is present.'''
221 221 res = False
222 222 for prefix in prefixes:
223 223 for i in xrange(len(self.comments)):
224 224 if self.comments[i].startswith(prefix):
225 225 self.comments[i] = prefix + new
226 226 res = True
227 227 break
228 228 return res
229 229
230 230 def _hasheader(self, prefixes):
231 231 '''Check if a header starts with any of the given prefixes.'''
232 232 for prefix in prefixes:
233 233 for comment in self.comments:
234 234 if comment.startswith(prefix):
235 235 return True
236 236 return False
237 237
238 238 def __str__(self):
239 239 if not self.comments:
240 240 return ''
241 241 return '\n'.join(self.comments) + '\n\n'
242 242
243 243 def _delmsg(self):
244 244 '''Remove existing message, keeping the rest of the comments fields.
245 245 If comments contains 'subject: ', message will prepend
246 246 the field and a blank line.'''
247 247 if self.message:
248 248 subj = 'subject: ' + self.message[0].lower()
249 249 for i in xrange(len(self.comments)):
250 250 if subj == self.comments[i].lower():
251 251 del self.comments[i]
252 252 self.message = self.message[2:]
253 253 break
254 254 ci = 0
255 255 for mi in self.message:
256 256 while mi != self.comments[ci]:
257 257 ci += 1
258 258 del self.comments[ci]
259 259
260 260 def newcommit(repo, phase, *args, **kwargs):
261 261 """helper dedicated to ensure a commit respect mq.secret setting
262 262
263 263 It should be used instead of repo.commit inside the mq source for operation
264 264 creating new changeset.
265 265 """
266 266 if phase is None:
267 267 if repo.ui.configbool('mq', 'secret', False):
268 268 phase = phases.secret
269 269 if phase is not None:
270 270 backup = repo.ui.backupconfig('phases', 'new-commit')
271 271 # Marking the repository as committing an mq patch can be used
272 272 # to optimize operations like _branchtags().
273 273 repo._committingpatch = True
274 274 try:
275 275 if phase is not None:
276 276 repo.ui.setconfig('phases', 'new-commit', phase)
277 277 return repo.commit(*args, **kwargs)
278 278 finally:
279 279 repo._committingpatch = False
280 280 if phase is not None:
281 281 repo.ui.restoreconfig(backup)
282 282
283 283 class queue(object):
284 284 def __init__(self, ui, path, patchdir=None):
285 285 self.basepath = path
286 286 try:
287 287 fh = open(os.path.join(path, 'patches.queue'))
288 288 cur = fh.read().rstrip()
289 289 fh.close()
290 290 if not cur:
291 291 curpath = os.path.join(path, 'patches')
292 292 else:
293 293 curpath = os.path.join(path, 'patches-' + cur)
294 294 except IOError:
295 295 curpath = os.path.join(path, 'patches')
296 296 self.path = patchdir or curpath
297 297 self.opener = scmutil.opener(self.path)
298 298 self.ui = ui
299 299 self.applieddirty = False
300 300 self.seriesdirty = False
301 301 self.added = []
302 302 self.seriespath = "series"
303 303 self.statuspath = "status"
304 304 self.guardspath = "guards"
305 305 self.activeguards = None
306 306 self.guardsdirty = False
307 307 # Handle mq.git as a bool with extended values
308 308 try:
309 309 gitmode = ui.configbool('mq', 'git', None)
310 310 if gitmode is None:
311 311 raise error.ConfigError()
312 312 self.gitmode = gitmode and 'yes' or 'no'
313 313 except error.ConfigError:
314 314 self.gitmode = ui.config('mq', 'git', 'auto').lower()
315 315 self.plainmode = ui.configbool('mq', 'plain', False)
316 316
317 317 @util.propertycache
318 318 def applied(self):
319 319 def parselines(lines):
320 320 for l in lines:
321 321 entry = l.split(':', 1)
322 322 if len(entry) > 1:
323 323 n, name = entry
324 324 yield statusentry(bin(n), name)
325 325 elif l.strip():
326 326 self.ui.warn(_('malformated mq status line: %s\n') % entry)
327 327 # else we ignore empty lines
328 328 try:
329 329 lines = self.opener.read(self.statuspath).splitlines()
330 330 return list(parselines(lines))
331 331 except IOError, e:
332 332 if e.errno == errno.ENOENT:
333 333 return []
334 334 raise
335 335
336 336 @util.propertycache
337 337 def fullseries(self):
338 338 try:
339 339 return self.opener.read(self.seriespath).splitlines()
340 340 except IOError, e:
341 341 if e.errno == errno.ENOENT:
342 342 return []
343 343 raise
344 344
345 345 @util.propertycache
346 346 def series(self):
347 347 self.parseseries()
348 348 return self.series
349 349
350 350 @util.propertycache
351 351 def seriesguards(self):
352 352 self.parseseries()
353 353 return self.seriesguards
354 354
355 355 def invalidate(self):
356 356 for a in 'applied fullseries series seriesguards'.split():
357 357 if a in self.__dict__:
358 358 delattr(self, a)
359 359 self.applieddirty = False
360 360 self.seriesdirty = False
361 361 self.guardsdirty = False
362 362 self.activeguards = None
363 363
364 364 def diffopts(self, opts={}, patchfn=None):
365 365 diffopts = patchmod.diffopts(self.ui, opts)
366 366 if self.gitmode == 'auto':
367 367 diffopts.upgrade = True
368 368 elif self.gitmode == 'keep':
369 369 pass
370 370 elif self.gitmode in ('yes', 'no'):
371 371 diffopts.git = self.gitmode == 'yes'
372 372 else:
373 373 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
374 374 ' got %s') % self.gitmode)
375 375 if patchfn:
376 376 diffopts = self.patchopts(diffopts, patchfn)
377 377 return diffopts
378 378
379 379 def patchopts(self, diffopts, *patches):
380 380 """Return a copy of input diff options with git set to true if
381 381 referenced patch is a git patch and should be preserved as such.
382 382 """
383 383 diffopts = diffopts.copy()
384 384 if not diffopts.git and self.gitmode == 'keep':
385 385 for patchfn in patches:
386 386 patchf = self.opener(patchfn, 'r')
387 387 # if the patch was a git patch, refresh it as a git patch
388 388 for line in patchf:
389 389 if line.startswith('diff --git'):
390 390 diffopts.git = True
391 391 break
392 392 patchf.close()
393 393 return diffopts
394 394
395 395 def join(self, *p):
396 396 return os.path.join(self.path, *p)
397 397
398 398 def findseries(self, patch):
399 399 def matchpatch(l):
400 400 l = l.split('#', 1)[0]
401 401 return l.strip() == patch
402 402 for index, l in enumerate(self.fullseries):
403 403 if matchpatch(l):
404 404 return index
405 405 return None
406 406
407 407 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
408 408
409 409 def parseseries(self):
410 410 self.series = []
411 411 self.seriesguards = []
412 412 for l in self.fullseries:
413 413 h = l.find('#')
414 414 if h == -1:
415 415 patch = l
416 416 comment = ''
417 417 elif h == 0:
418 418 continue
419 419 else:
420 420 patch = l[:h]
421 421 comment = l[h:]
422 422 patch = patch.strip()
423 423 if patch:
424 424 if patch in self.series:
425 425 raise util.Abort(_('%s appears more than once in %s') %
426 426 (patch, self.join(self.seriespath)))
427 427 self.series.append(patch)
428 428 self.seriesguards.append(self.guard_re.findall(comment))
429 429
430 430 def checkguard(self, guard):
431 431 if not guard:
432 432 return _('guard cannot be an empty string')
433 433 bad_chars = '# \t\r\n\f'
434 434 first = guard[0]
435 435 if first in '-+':
436 436 return (_('guard %r starts with invalid character: %r') %
437 437 (guard, first))
438 438 for c in bad_chars:
439 439 if c in guard:
440 440 return _('invalid character in guard %r: %r') % (guard, c)
441 441
442 442 def setactive(self, guards):
443 443 for guard in guards:
444 444 bad = self.checkguard(guard)
445 445 if bad:
446 446 raise util.Abort(bad)
447 447 guards = sorted(set(guards))
448 448 self.ui.debug('active guards: %s\n' % ' '.join(guards))
449 449 self.activeguards = guards
450 450 self.guardsdirty = True
451 451
452 452 def active(self):
453 453 if self.activeguards is None:
454 454 self.activeguards = []
455 455 try:
456 456 guards = self.opener.read(self.guardspath).split()
457 457 except IOError, err:
458 458 if err.errno != errno.ENOENT:
459 459 raise
460 460 guards = []
461 461 for i, guard in enumerate(guards):
462 462 bad = self.checkguard(guard)
463 463 if bad:
464 464 self.ui.warn('%s:%d: %s\n' %
465 465 (self.join(self.guardspath), i + 1, bad))
466 466 else:
467 467 self.activeguards.append(guard)
468 468 return self.activeguards
469 469
470 470 def setguards(self, idx, guards):
471 471 for g in guards:
472 472 if len(g) < 2:
473 473 raise util.Abort(_('guard %r too short') % g)
474 474 if g[0] not in '-+':
475 475 raise util.Abort(_('guard %r starts with invalid char') % g)
476 476 bad = self.checkguard(g[1:])
477 477 if bad:
478 478 raise util.Abort(bad)
479 479 drop = self.guard_re.sub('', self.fullseries[idx])
480 480 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
481 481 self.parseseries()
482 482 self.seriesdirty = True
483 483
484 484 def pushable(self, idx):
485 485 if isinstance(idx, str):
486 486 idx = self.series.index(idx)
487 487 patchguards = self.seriesguards[idx]
488 488 if not patchguards:
489 489 return True, None
490 490 guards = self.active()
491 491 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
492 492 if exactneg:
493 493 return False, repr(exactneg[0])
494 494 pos = [g for g in patchguards if g[0] == '+']
495 495 exactpos = [g for g in pos if g[1:] in guards]
496 496 if pos:
497 497 if exactpos:
498 498 return True, repr(exactpos[0])
499 499 return False, ' '.join(map(repr, pos))
500 500 return True, ''
501 501
502 502 def explainpushable(self, idx, all_patches=False):
503 503 write = all_patches and self.ui.write or self.ui.warn
504 504 if all_patches or self.ui.verbose:
505 505 if isinstance(idx, str):
506 506 idx = self.series.index(idx)
507 507 pushable, why = self.pushable(idx)
508 508 if all_patches and pushable:
509 509 if why is None:
510 510 write(_('allowing %s - no guards in effect\n') %
511 511 self.series[idx])
512 512 else:
513 513 if not why:
514 514 write(_('allowing %s - no matching negative guards\n') %
515 515 self.series[idx])
516 516 else:
517 517 write(_('allowing %s - guarded by %s\n') %
518 518 (self.series[idx], why))
519 519 if not pushable:
520 520 if why:
521 521 write(_('skipping %s - guarded by %s\n') %
522 522 (self.series[idx], why))
523 523 else:
524 524 write(_('skipping %s - no matching guards\n') %
525 525 self.series[idx])
526 526
527 527 def savedirty(self):
528 528 def writelist(items, path):
529 529 fp = self.opener(path, 'w')
530 530 for i in items:
531 531 fp.write("%s\n" % i)
532 532 fp.close()
533 533 if self.applieddirty:
534 534 writelist(map(str, self.applied), self.statuspath)
535 535 self.applieddirty = False
536 536 if self.seriesdirty:
537 537 writelist(self.fullseries, self.seriespath)
538 538 self.seriesdirty = False
539 539 if self.guardsdirty:
540 540 writelist(self.activeguards, self.guardspath)
541 541 self.guardsdirty = False
542 542 if self.added:
543 543 qrepo = self.qrepo()
544 544 if qrepo:
545 545 qrepo[None].add(f for f in self.added if f not in qrepo[None])
546 546 self.added = []
547 547
548 548 def removeundo(self, repo):
549 549 undo = repo.sjoin('undo')
550 550 if not os.path.exists(undo):
551 551 return
552 552 try:
553 553 os.unlink(undo)
554 554 except OSError, inst:
555 555 self.ui.warn(_('error removing undo: %s\n') % str(inst))
556 556
557 557 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
558 558 fp=None, changes=None, opts={}):
559 559 stat = opts.get('stat')
560 560 m = scmutil.match(repo[node1], files, opts)
561 561 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
562 562 changes, stat, fp)
563 563
564 564 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
565 565 # first try just applying the patch
566 566 (err, n) = self.apply(repo, [patch], update_status=False,
567 567 strict=True, merge=rev)
568 568
569 569 if err == 0:
570 570 return (err, n)
571 571
572 572 if n is None:
573 573 raise util.Abort(_("apply failed for patch %s") % patch)
574 574
575 575 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
576 576
577 577 # apply failed, strip away that rev and merge.
578 578 hg.clean(repo, head)
579 579 self.strip(repo, [n], update=False, backup='strip')
580 580
581 581 ctx = repo[rev]
582 582 ret = hg.merge(repo, rev)
583 583 if ret:
584 584 raise util.Abort(_("update returned %d") % ret)
585 585 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
586 586 if n is None:
587 587 raise util.Abort(_("repo commit failed"))
588 588 try:
589 589 ph = patchheader(mergeq.join(patch), self.plainmode)
590 590 except:
591 591 raise util.Abort(_("unable to read %s") % patch)
592 592
593 593 diffopts = self.patchopts(diffopts, patch)
594 594 patchf = self.opener(patch, "w")
595 595 comments = str(ph)
596 596 if comments:
597 597 patchf.write(comments)
598 598 self.printdiff(repo, diffopts, head, n, fp=patchf)
599 599 patchf.close()
600 600 self.removeundo(repo)
601 601 return (0, n)
602 602
603 603 def qparents(self, repo, rev=None):
604 604 if rev is None:
605 605 (p1, p2) = repo.dirstate.parents()
606 606 if p2 == nullid:
607 607 return p1
608 608 if not self.applied:
609 609 return None
610 610 return self.applied[-1].node
611 611 p1, p2 = repo.changelog.parents(rev)
612 612 if p2 != nullid and p2 in [x.node for x in self.applied]:
613 613 return p2
614 614 return p1
615 615
616 616 def mergepatch(self, repo, mergeq, series, diffopts):
617 617 if not self.applied:
618 618 # each of the patches merged in will have two parents. This
619 619 # can confuse the qrefresh, qdiff, and strip code because it
620 620 # needs to know which parent is actually in the patch queue.
621 621 # so, we insert a merge marker with only one parent. This way
622 622 # the first patch in the queue is never a merge patch
623 623 #
624 624 pname = ".hg.patches.merge.marker"
625 625 n = newcommit(repo, None, '[mq]: merge marker', force=True)
626 626 self.removeundo(repo)
627 627 self.applied.append(statusentry(n, pname))
628 628 self.applieddirty = True
629 629
630 630 head = self.qparents(repo)
631 631
632 632 for patch in series:
633 633 patch = mergeq.lookup(patch, strict=True)
634 634 if not patch:
635 635 self.ui.warn(_("patch %s does not exist\n") % patch)
636 636 return (1, None)
637 637 pushable, reason = self.pushable(patch)
638 638 if not pushable:
639 639 self.explainpushable(patch, all_patches=True)
640 640 continue
641 641 info = mergeq.isapplied(patch)
642 642 if not info:
643 643 self.ui.warn(_("patch %s is not applied\n") % patch)
644 644 return (1, None)
645 645 rev = info[1]
646 646 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
647 647 if head:
648 648 self.applied.append(statusentry(head, patch))
649 649 self.applieddirty = True
650 650 if err:
651 651 return (err, head)
652 652 self.savedirty()
653 653 return (0, head)
654 654
655 655 def patch(self, repo, patchfile):
656 656 '''Apply patchfile to the working directory.
657 657 patchfile: name of patch file'''
658 658 files = set()
659 659 try:
660 660 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
661 661 files=files, eolmode=None)
662 662 return (True, list(files), fuzz)
663 663 except Exception, inst:
664 664 self.ui.note(str(inst) + '\n')
665 665 if not self.ui.verbose:
666 666 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
667 667 self.ui.traceback()
668 668 return (False, list(files), False)
669 669
670 670 def apply(self, repo, series, list=False, update_status=True,
671 671 strict=False, patchdir=None, merge=None, all_files=None):
672 672 wlock = lock = tr = None
673 673 try:
674 674 wlock = repo.wlock()
675 675 lock = repo.lock()
676 676 tr = repo.transaction("qpush")
677 677 try:
678 678 ret = self._apply(repo, series, list, update_status,
679 679 strict, patchdir, merge, all_files=all_files)
680 680 tr.close()
681 681 self.savedirty()
682 682 return ret
683 683 except:
684 684 try:
685 685 tr.abort()
686 686 finally:
687 687 repo.invalidate()
688 688 repo.dirstate.invalidate()
689 689 self.invalidate()
690 690 raise
691 691 finally:
692 692 release(tr, lock, wlock)
693 693 self.removeundo(repo)
694 694
695 695 def _apply(self, repo, series, list=False, update_status=True,
696 696 strict=False, patchdir=None, merge=None, all_files=None):
697 697 '''returns (error, hash)
698 698 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
699 699 # TODO unify with commands.py
700 700 if not patchdir:
701 701 patchdir = self.path
702 702 err = 0
703 703 n = None
704 704 for patchname in series:
705 705 pushable, reason = self.pushable(patchname)
706 706 if not pushable:
707 707 self.explainpushable(patchname, all_patches=True)
708 708 continue
709 709 self.ui.status(_("applying %s\n") % patchname)
710 710 pf = os.path.join(patchdir, patchname)
711 711
712 712 try:
713 713 ph = patchheader(self.join(patchname), self.plainmode)
714 714 except IOError:
715 715 self.ui.warn(_("unable to read %s\n") % patchname)
716 716 err = 1
717 717 break
718 718
719 719 message = ph.message
720 720 if not message:
721 721 # The commit message should not be translated
722 722 message = "imported patch %s\n" % patchname
723 723 else:
724 724 if list:
725 725 # The commit message should not be translated
726 726 message.append("\nimported patch %s" % patchname)
727 727 message = '\n'.join(message)
728 728
729 729 if ph.haspatch:
730 730 (patcherr, files, fuzz) = self.patch(repo, pf)
731 731 if all_files is not None:
732 732 all_files.update(files)
733 733 patcherr = not patcherr
734 734 else:
735 735 self.ui.warn(_("patch %s is empty\n") % patchname)
736 736 patcherr, files, fuzz = 0, [], 0
737 737
738 738 if merge and files:
739 739 # Mark as removed/merged and update dirstate parent info
740 740 removed = []
741 741 merged = []
742 742 for f in files:
743 743 if os.path.lexists(repo.wjoin(f)):
744 744 merged.append(f)
745 745 else:
746 746 removed.append(f)
747 747 for f in removed:
748 748 repo.dirstate.remove(f)
749 749 for f in merged:
750 750 repo.dirstate.merge(f)
751 751 p1, p2 = repo.dirstate.parents()
752 752 repo.dirstate.setparents(p1, merge)
753 753
754 754 match = scmutil.matchfiles(repo, files or [])
755 755 oldtip = repo['tip']
756 756 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
757 757 force=True)
758 758 if repo['tip'] == oldtip:
759 759 raise util.Abort(_("qpush exactly duplicates child changeset"))
760 760 if n is None:
761 761 raise util.Abort(_("repository commit failed"))
762 762
763 763 if update_status:
764 764 self.applied.append(statusentry(n, patchname))
765 765
766 766 if patcherr:
767 767 self.ui.warn(_("patch failed, rejects left in working dir\n"))
768 768 err = 2
769 769 break
770 770
771 771 if fuzz and strict:
772 772 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
773 773 err = 3
774 774 break
775 775 return (err, n)
776 776
777 777 def _cleanup(self, patches, numrevs, keep=False):
778 778 if not keep:
779 779 r = self.qrepo()
780 780 if r:
781 781 r[None].forget(patches)
782 782 for p in patches:
783 783 os.unlink(self.join(p))
784 784
785 785 qfinished = []
786 786 if numrevs:
787 787 qfinished = self.applied[:numrevs]
788 788 del self.applied[:numrevs]
789 789 self.applieddirty = True
790 790
791 791 unknown = []
792 792
793 793 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
794 794 reverse=True):
795 795 if i is not None:
796 796 del self.fullseries[i]
797 797 else:
798 798 unknown.append(p)
799 799
800 800 if unknown:
801 801 if numrevs:
802 802 rev = dict((entry.name, entry.node) for entry in qfinished)
803 803 for p in unknown:
804 804 msg = _('revision %s refers to unknown patches: %s\n')
805 805 self.ui.warn(msg % (short(rev[p]), p))
806 806 else:
807 807 msg = _('unknown patches: %s\n')
808 808 raise util.Abort(''.join(msg % p for p in unknown))
809 809
810 810 self.parseseries()
811 811 self.seriesdirty = True
812 812 return [entry.node for entry in qfinished]
813 813
814 814 def _revpatches(self, repo, revs):
815 815 firstrev = repo[self.applied[0].node].rev()
816 816 patches = []
817 817 for i, rev in enumerate(revs):
818 818
819 819 if rev < firstrev:
820 820 raise util.Abort(_('revision %d is not managed') % rev)
821 821
822 822 ctx = repo[rev]
823 823 base = self.applied[i].node
824 824 if ctx.node() != base:
825 825 msg = _('cannot delete revision %d above applied patches')
826 826 raise util.Abort(msg % rev)
827 827
828 828 patch = self.applied[i].name
829 829 for fmt in ('[mq]: %s', 'imported patch %s'):
830 830 if ctx.description() == fmt % patch:
831 831 msg = _('patch %s finalized without changeset message\n')
832 832 repo.ui.status(msg % patch)
833 833 break
834 834
835 835 patches.append(patch)
836 836 return patches
837 837
838 838 def finish(self, repo, revs):
839 839 # Manually trigger phase computation to ensure phasedefaults is
840 840 # executed before we remove the patches.
841 841 repo._phaserev
842 842 patches = self._revpatches(repo, sorted(revs))
843 843 qfinished = self._cleanup(patches, len(patches))
844 844 if qfinished and repo.ui.configbool('mq', 'secret', False):
845 845 # only use this logic when the secret option is added
846 846 oldqbase = repo[qfinished[0]]
847 847 if oldqbase.p1().phase() < phases.secret:
848 848 phases.advanceboundary(repo, phases.draft, qfinished)
849 849
850 850 def delete(self, repo, patches, opts):
851 851 if not patches and not opts.get('rev'):
852 852 raise util.Abort(_('qdelete requires at least one revision or '
853 853 'patch name'))
854 854
855 855 realpatches = []
856 856 for patch in patches:
857 857 patch = self.lookup(patch, strict=True)
858 858 info = self.isapplied(patch)
859 859 if info:
860 860 raise util.Abort(_("cannot delete applied patch %s") % patch)
861 861 if patch not in self.series:
862 862 raise util.Abort(_("patch %s not in series file") % patch)
863 863 if patch not in realpatches:
864 864 realpatches.append(patch)
865 865
866 866 numrevs = 0
867 867 if opts.get('rev'):
868 868 if not self.applied:
869 869 raise util.Abort(_('no patches applied'))
870 870 revs = scmutil.revrange(repo, opts.get('rev'))
871 871 if len(revs) > 1 and revs[0] > revs[1]:
872 872 revs.reverse()
873 873 revpatches = self._revpatches(repo, revs)
874 874 realpatches += revpatches
875 875 numrevs = len(revpatches)
876 876
877 877 self._cleanup(realpatches, numrevs, opts.get('keep'))
878 878
879 879 def checktoppatch(self, repo):
880 880 if self.applied:
881 881 top = self.applied[-1].node
882 882 patch = self.applied[-1].name
883 883 pp = repo.dirstate.parents()
884 884 if top not in pp:
885 885 raise util.Abort(_("working directory revision is not qtip"))
886 886 return top, patch
887 887 return None, None
888 888
889 889 def checksubstate(self, repo):
890 890 '''return list of subrepos at a different revision than substate.
891 891 Abort if any subrepos have uncommitted changes.'''
892 892 inclsubs = []
893 893 wctx = repo[None]
894 894 for s in wctx.substate:
895 895 if wctx.sub(s).dirty(True):
896 896 raise util.Abort(
897 897 _("uncommitted changes in subrepository %s") % s)
898 898 elif wctx.sub(s).dirty():
899 899 inclsubs.append(s)
900 900 return inclsubs
901 901
902 902 def localchangesfound(self, refresh=True):
903 903 if refresh:
904 904 raise util.Abort(_("local changes found, refresh first"))
905 905 else:
906 906 raise util.Abort(_("local changes found"))
907 907
908 908 def checklocalchanges(self, repo, force=False, refresh=True):
909 909 m, a, r, d = repo.status()[:4]
910 910 if (m or a or r or d) and not force:
911 911 self.localchangesfound(refresh)
912 912 return m, a, r, d
913 913
914 914 _reserved = ('series', 'status', 'guards', '.', '..')
915 915 def checkreservedname(self, name):
916 916 if name in self._reserved:
917 917 raise util.Abort(_('"%s" cannot be used as the name of a patch')
918 918 % name)
919 919 for prefix in ('.hg', '.mq'):
920 920 if name.startswith(prefix):
921 921 raise util.Abort(_('patch name cannot begin with "%s"')
922 922 % prefix)
923 923 for c in ('#', ':'):
924 924 if c in name:
925 925 raise util.Abort(_('"%s" cannot be used in the name of a patch')
926 926 % c)
927 927
928 928 def checkpatchname(self, name, force=False):
929 929 self.checkreservedname(name)
930 930 if not force and os.path.exists(self.join(name)):
931 931 if os.path.isdir(self.join(name)):
932 932 raise util.Abort(_('"%s" already exists as a directory')
933 933 % name)
934 934 else:
935 935 raise util.Abort(_('patch "%s" already exists') % name)
936 936
937 937 def new(self, repo, patchfn, *pats, **opts):
938 938 """options:
939 939 msg: a string or a no-argument function returning a string
940 940 """
941 941 msg = opts.get('msg')
942 942 user = opts.get('user')
943 943 date = opts.get('date')
944 944 if date:
945 945 date = util.parsedate(date)
946 946 diffopts = self.diffopts({'git': opts.get('git')})
947 947 if opts.get('checkname', True):
948 948 self.checkpatchname(patchfn)
949 949 inclsubs = self.checksubstate(repo)
950 950 if inclsubs:
951 951 inclsubs.append('.hgsubstate')
952 952 if opts.get('include') or opts.get('exclude') or pats:
953 953 if inclsubs:
954 954 pats = list(pats or []) + inclsubs
955 955 match = scmutil.match(repo[None], pats, opts)
956 956 # detect missing files in pats
957 957 def badfn(f, msg):
958 958 if f != '.hgsubstate': # .hgsubstate is auto-created
959 959 raise util.Abort('%s: %s' % (f, msg))
960 960 match.bad = badfn
961 961 m, a, r, d = repo.status(match=match)[:4]
962 962 else:
963 963 m, a, r, d = self.checklocalchanges(repo, force=True)
964 964 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
965 965 if len(repo[None].parents()) > 1:
966 966 raise util.Abort(_('cannot manage merge changesets'))
967 967 commitfiles = m + a + r
968 968 self.checktoppatch(repo)
969 969 insert = self.fullseriesend()
970 970 wlock = repo.wlock()
971 971 try:
972 972 try:
973 973 # if patch file write fails, abort early
974 974 p = self.opener(patchfn, "w")
975 975 except IOError, e:
976 976 raise util.Abort(_('cannot write patch "%s": %s')
977 977 % (patchfn, e.strerror))
978 978 try:
979 979 if self.plainmode:
980 980 if user:
981 981 p.write("From: " + user + "\n")
982 982 if not date:
983 983 p.write("\n")
984 984 if date:
985 985 p.write("Date: %d %d\n\n" % date)
986 986 else:
987 987 p.write("# HG changeset patch\n")
988 988 p.write("# Parent "
989 989 + hex(repo[None].p1().node()) + "\n")
990 990 if user:
991 991 p.write("# User " + user + "\n")
992 992 if date:
993 993 p.write("# Date %s %s\n\n" % date)
994 994 if util.safehasattr(msg, '__call__'):
995 995 msg = msg()
996 996 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
997 997 n = newcommit(repo, None, commitmsg, user, date, match=match,
998 998 force=True)
999 999 if n is None:
1000 1000 raise util.Abort(_("repo commit failed"))
1001 1001 try:
1002 1002 self.fullseries[insert:insert] = [patchfn]
1003 1003 self.applied.append(statusentry(n, patchfn))
1004 1004 self.parseseries()
1005 1005 self.seriesdirty = True
1006 1006 self.applieddirty = True
1007 1007 if msg:
1008 1008 msg = msg + "\n\n"
1009 1009 p.write(msg)
1010 1010 if commitfiles:
1011 1011 parent = self.qparents(repo, n)
1012 1012 chunks = patchmod.diff(repo, node1=parent, node2=n,
1013 1013 match=match, opts=diffopts)
1014 1014 for chunk in chunks:
1015 1015 p.write(chunk)
1016 1016 p.close()
1017 1017 r = self.qrepo()
1018 1018 if r:
1019 1019 r[None].add([patchfn])
1020 1020 except:
1021 1021 repo.rollback()
1022 1022 raise
1023 1023 except Exception:
1024 1024 patchpath = self.join(patchfn)
1025 1025 try:
1026 1026 os.unlink(patchpath)
1027 1027 except:
1028 1028 self.ui.warn(_('error unlinking %s\n') % patchpath)
1029 1029 raise
1030 1030 self.removeundo(repo)
1031 1031 finally:
1032 1032 release(wlock)
1033 1033
1034 1034 def strip(self, repo, revs, update=True, backup="all", force=None):
1035 1035 wlock = lock = None
1036 1036 try:
1037 1037 wlock = repo.wlock()
1038 1038 lock = repo.lock()
1039 1039
1040 1040 if update:
1041 1041 self.checklocalchanges(repo, force=force, refresh=False)
1042 1042 urev = self.qparents(repo, revs[0])
1043 1043 hg.clean(repo, urev)
1044 1044 repo.dirstate.write()
1045 1045
1046 1046 repair.strip(self.ui, repo, revs, backup)
1047 1047 finally:
1048 1048 release(lock, wlock)
1049 1049
1050 1050 def isapplied(self, patch):
1051 1051 """returns (index, rev, patch)"""
1052 1052 for i, a in enumerate(self.applied):
1053 1053 if a.name == patch:
1054 1054 return (i, a.node, a.name)
1055 1055 return None
1056 1056
1057 1057 # if the exact patch name does not exist, we try a few
1058 1058 # variations. If strict is passed, we try only #1
1059 1059 #
1060 1060 # 1) a number (as string) to indicate an offset in the series file
1061 1061 # 2) a unique substring of the patch name was given
1062 1062 # 3) patchname[-+]num to indicate an offset in the series file
1063 1063 def lookup(self, patch, strict=False):
1064 1064 def partialname(s):
1065 1065 if s in self.series:
1066 1066 return s
1067 1067 matches = [x for x in self.series if s in x]
1068 1068 if len(matches) > 1:
1069 1069 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1070 1070 for m in matches:
1071 1071 self.ui.warn(' %s\n' % m)
1072 1072 return None
1073 1073 if matches:
1074 1074 return matches[0]
1075 1075 if self.series and self.applied:
1076 1076 if s == 'qtip':
1077 1077 return self.series[self.seriesend(True)-1]
1078 1078 if s == 'qbase':
1079 1079 return self.series[0]
1080 1080 return None
1081 1081
1082 1082 if patch in self.series:
1083 1083 return patch
1084 1084
1085 1085 if not os.path.isfile(self.join(patch)):
1086 1086 try:
1087 1087 sno = int(patch)
1088 1088 except (ValueError, OverflowError):
1089 1089 pass
1090 1090 else:
1091 1091 if -len(self.series) <= sno < len(self.series):
1092 1092 return self.series[sno]
1093 1093
1094 1094 if not strict:
1095 1095 res = partialname(patch)
1096 1096 if res:
1097 1097 return res
1098 1098 minus = patch.rfind('-')
1099 1099 if minus >= 0:
1100 1100 res = partialname(patch[:minus])
1101 1101 if res:
1102 1102 i = self.series.index(res)
1103 1103 try:
1104 1104 off = int(patch[minus + 1:] or 1)
1105 1105 except (ValueError, OverflowError):
1106 1106 pass
1107 1107 else:
1108 1108 if i - off >= 0:
1109 1109 return self.series[i - off]
1110 1110 plus = patch.rfind('+')
1111 1111 if plus >= 0:
1112 1112 res = partialname(patch[:plus])
1113 1113 if res:
1114 1114 i = self.series.index(res)
1115 1115 try:
1116 1116 off = int(patch[plus + 1:] or 1)
1117 1117 except (ValueError, OverflowError):
1118 1118 pass
1119 1119 else:
1120 1120 if i + off < len(self.series):
1121 1121 return self.series[i + off]
1122 1122 raise util.Abort(_("patch %s not in series") % patch)
1123 1123
1124 1124 def push(self, repo, patch=None, force=False, list=False,
1125 1125 mergeq=None, all=False, move=False, exact=False):
1126 1126 diffopts = self.diffopts()
1127 1127 wlock = repo.wlock()
1128 1128 try:
1129 1129 heads = []
1130 1130 for b, ls in repo.branchmap().iteritems():
1131 1131 heads += ls
1132 1132 if not heads:
1133 1133 heads = [nullid]
1134 1134 if repo.dirstate.p1() not in heads and not exact:
1135 1135 self.ui.status(_("(working directory not at a head)\n"))
1136 1136
1137 1137 if not self.series:
1138 1138 self.ui.warn(_('no patches in series\n'))
1139 1139 return 0
1140 1140
1141 1141 # Suppose our series file is: A B C and the current 'top'
1142 1142 # patch is B. qpush C should be performed (moving forward)
1143 1143 # qpush B is a NOP (no change) qpush A is an error (can't
1144 1144 # go backwards with qpush)
1145 1145 if patch:
1146 1146 patch = self.lookup(patch)
1147 1147 info = self.isapplied(patch)
1148 1148 if info and info[0] >= len(self.applied) - 1:
1149 1149 self.ui.warn(
1150 1150 _('qpush: %s is already at the top\n') % patch)
1151 1151 return 0
1152 1152
1153 1153 pushable, reason = self.pushable(patch)
1154 1154 if pushable:
1155 1155 if self.series.index(patch) < self.seriesend():
1156 1156 raise util.Abort(
1157 1157 _("cannot push to a previous patch: %s") % patch)
1158 1158 else:
1159 1159 if reason:
1160 1160 reason = _('guarded by %s') % reason
1161 1161 else:
1162 1162 reason = _('no matching guards')
1163 1163 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1164 1164 return 1
1165 1165 elif all:
1166 1166 patch = self.series[-1]
1167 1167 if self.isapplied(patch):
1168 1168 self.ui.warn(_('all patches are currently applied\n'))
1169 1169 return 0
1170 1170
1171 1171 # Following the above example, starting at 'top' of B:
1172 1172 # qpush should be performed (pushes C), but a subsequent
1173 1173 # qpush without an argument is an error (nothing to
1174 1174 # apply). This allows a loop of "...while hg qpush..." to
1175 1175 # work as it detects an error when done
1176 1176 start = self.seriesend()
1177 1177 if start == len(self.series):
1178 1178 self.ui.warn(_('patch series already fully applied\n'))
1179 1179 return 1
1180 1180 if not force:
1181 1181 self.checklocalchanges(repo, refresh=self.applied)
1182 1182
1183 1183 if exact:
1184 1184 if move:
1185 1185 raise util.Abort(_("cannot use --exact and --move together"))
1186 1186 if self.applied:
1187 1187 raise util.Abort(_("cannot push --exact with applied patches"))
1188 1188 root = self.series[start]
1189 1189 target = patchheader(self.join(root), self.plainmode).parent
1190 1190 if not target:
1191 1191 raise util.Abort(
1192 1192 _("%s does not have a parent recorded") % root)
1193 1193 if not repo[target] == repo['.']:
1194 1194 hg.update(repo, target)
1195 1195
1196 1196 if move:
1197 1197 if not patch:
1198 1198 raise util.Abort(_("please specify the patch to move"))
1199 1199 for i, rpn in enumerate(self.fullseries[start:]):
1200 1200 # strip markers for patch guards
1201 1201 if self.guard_re.split(rpn, 1)[0] == patch:
1202 1202 break
1203 1203 index = start + i
1204 1204 assert index < len(self.fullseries)
1205 1205 fullpatch = self.fullseries[index]
1206 1206 del self.fullseries[index]
1207 1207 self.fullseries.insert(start, fullpatch)
1208 1208 self.parseseries()
1209 1209 self.seriesdirty = True
1210 1210
1211 1211 self.applieddirty = True
1212 1212 if start > 0:
1213 1213 self.checktoppatch(repo)
1214 1214 if not patch:
1215 1215 patch = self.series[start]
1216 1216 end = start + 1
1217 1217 else:
1218 1218 end = self.series.index(patch, start) + 1
1219 1219
1220 1220 s = self.series[start:end]
1221 1221 all_files = set()
1222 1222 try:
1223 1223 if mergeq:
1224 1224 ret = self.mergepatch(repo, mergeq, s, diffopts)
1225 1225 else:
1226 1226 ret = self.apply(repo, s, list, all_files=all_files)
1227 1227 except:
1228 1228 self.ui.warn(_('cleaning up working directory...'))
1229 1229 node = repo.dirstate.p1()
1230 1230 hg.revert(repo, node, None)
1231 1231 # only remove unknown files that we know we touched or
1232 1232 # created while patching
1233 1233 for f in all_files:
1234 1234 if f not in repo.dirstate:
1235 1235 try:
1236 1236 util.unlinkpath(repo.wjoin(f))
1237 1237 except OSError, inst:
1238 1238 if inst.errno != errno.ENOENT:
1239 1239 raise
1240 1240 self.ui.warn(_('done\n'))
1241 1241 raise
1242 1242
1243 1243 if not self.applied:
1244 1244 return ret[0]
1245 1245 top = self.applied[-1].name
1246 1246 if ret[0] and ret[0] > 1:
1247 1247 msg = _("errors during apply, please fix and refresh %s\n")
1248 1248 self.ui.write(msg % top)
1249 1249 else:
1250 1250 self.ui.write(_("now at: %s\n") % top)
1251 1251 return ret[0]
1252 1252
1253 1253 finally:
1254 1254 wlock.release()
1255 1255
1256 1256 def pop(self, repo, patch=None, force=False, update=True, all=False):
1257 1257 wlock = repo.wlock()
1258 1258 try:
1259 1259 if patch:
1260 1260 # index, rev, patch
1261 1261 info = self.isapplied(patch)
1262 1262 if not info:
1263 1263 patch = self.lookup(patch)
1264 1264 info = self.isapplied(patch)
1265 1265 if not info:
1266 1266 raise util.Abort(_("patch %s is not applied") % patch)
1267 1267
1268 1268 if not self.applied:
1269 1269 # Allow qpop -a to work repeatedly,
1270 1270 # but not qpop without an argument
1271 1271 self.ui.warn(_("no patches applied\n"))
1272 1272 return not all
1273 1273
1274 1274 if all:
1275 1275 start = 0
1276 1276 elif patch:
1277 1277 start = info[0] + 1
1278 1278 else:
1279 1279 start = len(self.applied) - 1
1280 1280
1281 1281 if start >= len(self.applied):
1282 1282 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1283 1283 return
1284 1284
1285 1285 if not update:
1286 1286 parents = repo.dirstate.parents()
1287 1287 rr = [x.node for x in self.applied]
1288 1288 for p in parents:
1289 1289 if p in rr:
1290 1290 self.ui.warn(_("qpop: forcing dirstate update\n"))
1291 1291 update = True
1292 1292 else:
1293 1293 parents = [p.node() for p in repo[None].parents()]
1294 1294 needupdate = False
1295 1295 for entry in self.applied[start:]:
1296 1296 if entry.node in parents:
1297 1297 needupdate = True
1298 1298 break
1299 1299 update = needupdate
1300 1300
1301 1301 if not force and update:
1302 1302 self.checklocalchanges(repo)
1303 1303
1304 1304 self.applieddirty = True
1305 1305 end = len(self.applied)
1306 1306 rev = self.applied[start].node
1307 1307 if update:
1308 1308 top = self.checktoppatch(repo)[0]
1309 1309
1310 1310 try:
1311 1311 heads = repo.changelog.heads(rev)
1312 1312 except error.LookupError:
1313 1313 node = short(rev)
1314 1314 raise util.Abort(_('trying to pop unknown node %s') % node)
1315 1315
1316 1316 if heads != [self.applied[-1].node]:
1317 1317 raise util.Abort(_("popping would remove a revision not "
1318 1318 "managed by this patch queue"))
1319 1319 if not repo[self.applied[-1].node].mutable():
1320 1320 raise util.Abort(
1321 1321 _("popping would remove an immutable revision"),
1322 1322 hint=_('see "hg help phases" for details'))
1323 1323
1324 1324 # we know there are no local changes, so we can make a simplified
1325 1325 # form of hg.update.
1326 1326 if update:
1327 1327 qp = self.qparents(repo, rev)
1328 1328 ctx = repo[qp]
1329 1329 m, a, r, d = repo.status(qp, top)[:4]
1330 1330 if d:
1331 1331 raise util.Abort(_("deletions found between repo revs"))
1332 1332 for f in a:
1333 1333 try:
1334 1334 util.unlinkpath(repo.wjoin(f))
1335 1335 except OSError, e:
1336 1336 if e.errno != errno.ENOENT:
1337 1337 raise
1338 1338 repo.dirstate.drop(f)
1339 1339 for f in m + r:
1340 1340 fctx = ctx[f]
1341 1341 repo.wwrite(f, fctx.data(), fctx.flags())
1342 1342 repo.dirstate.normal(f)
1343 1343 repo.dirstate.setparents(qp, nullid)
1344 1344 for patch in reversed(self.applied[start:end]):
1345 1345 self.ui.status(_("popping %s\n") % patch.name)
1346 1346 del self.applied[start:end]
1347 1347 self.strip(repo, [rev], update=False, backup='strip')
1348 1348 if self.applied:
1349 1349 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1350 1350 else:
1351 1351 self.ui.write(_("patch queue now empty\n"))
1352 1352 finally:
1353 1353 wlock.release()
1354 1354
1355 1355 def diff(self, repo, pats, opts):
1356 1356 top, patch = self.checktoppatch(repo)
1357 1357 if not top:
1358 1358 self.ui.write(_("no patches applied\n"))
1359 1359 return
1360 1360 qp = self.qparents(repo, top)
1361 1361 if opts.get('reverse'):
1362 1362 node1, node2 = None, qp
1363 1363 else:
1364 1364 node1, node2 = qp, None
1365 1365 diffopts = self.diffopts(opts, patch)
1366 1366 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1367 1367
1368 1368 def refresh(self, repo, pats=None, **opts):
1369 1369 if not self.applied:
1370 1370 self.ui.write(_("no patches applied\n"))
1371 1371 return 1
1372 1372 msg = opts.get('msg', '').rstrip()
1373 1373 newuser = opts.get('user')
1374 1374 newdate = opts.get('date')
1375 1375 if newdate:
1376 1376 newdate = '%d %d' % util.parsedate(newdate)
1377 1377 wlock = repo.wlock()
1378 1378
1379 1379 try:
1380 1380 self.checktoppatch(repo)
1381 1381 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1382 1382 if repo.changelog.heads(top) != [top]:
1383 1383 raise util.Abort(_("cannot refresh a revision with children"))
1384 1384 if not repo[top].mutable():
1385 1385 raise util.Abort(_("cannot refresh immutable revision"),
1386 1386 hint=_('see "hg help phases" for details'))
1387 1387
1388 1388 inclsubs = self.checksubstate(repo)
1389 1389
1390 1390 cparents = repo.changelog.parents(top)
1391 1391 patchparent = self.qparents(repo, top)
1392 1392 ph = patchheader(self.join(patchfn), self.plainmode)
1393 1393 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1394 1394 if msg:
1395 1395 ph.setmessage(msg)
1396 1396 if newuser:
1397 1397 ph.setuser(newuser)
1398 1398 if newdate:
1399 1399 ph.setdate(newdate)
1400 1400 ph.setparent(hex(patchparent))
1401 1401
1402 1402 # only commit new patch when write is complete
1403 1403 patchf = self.opener(patchfn, 'w', atomictemp=True)
1404 1404
1405 1405 comments = str(ph)
1406 1406 if comments:
1407 1407 patchf.write(comments)
1408 1408
1409 1409 # update the dirstate in place, strip off the qtip commit
1410 1410 # and then commit.
1411 1411 #
1412 1412 # this should really read:
1413 1413 # mm, dd, aa = repo.status(top, patchparent)[:3]
1414 1414 # but we do it backwards to take advantage of manifest/chlog
1415 1415 # caching against the next repo.status call
1416 1416 mm, aa, dd = repo.status(patchparent, top)[:3]
1417 1417 changes = repo.changelog.read(top)
1418 1418 man = repo.manifest.read(changes[0])
1419 1419 aaa = aa[:]
1420 1420 matchfn = scmutil.match(repo[None], pats, opts)
1421 1421 # in short mode, we only diff the files included in the
1422 1422 # patch already plus specified files
1423 1423 if opts.get('short'):
1424 1424 # if amending a patch, we start with existing
1425 1425 # files plus specified files - unfiltered
1426 1426 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1427 1427 # filter with inc/exl options
1428 1428 matchfn = scmutil.match(repo[None], opts=opts)
1429 1429 else:
1430 1430 match = scmutil.matchall(repo)
1431 1431 m, a, r, d = repo.status(match=match)[:4]
1432 1432 mm = set(mm)
1433 1433 aa = set(aa)
1434 1434 dd = set(dd)
1435 1435
1436 1436 # we might end up with files that were added between
1437 1437 # qtip and the dirstate parent, but then changed in the
1438 1438 # local dirstate. in this case, we want them to only
1439 1439 # show up in the added section
1440 1440 for x in m:
1441 1441 if x not in aa:
1442 1442 mm.add(x)
1443 1443 # we might end up with files added by the local dirstate that
1444 1444 # were deleted by the patch. In this case, they should only
1445 1445 # show up in the changed section.
1446 1446 for x in a:
1447 1447 if x in dd:
1448 1448 dd.remove(x)
1449 1449 mm.add(x)
1450 1450 else:
1451 1451 aa.add(x)
1452 1452 # make sure any files deleted in the local dirstate
1453 1453 # are not in the add or change column of the patch
1454 1454 forget = []
1455 1455 for x in d + r:
1456 1456 if x in aa:
1457 1457 aa.remove(x)
1458 1458 forget.append(x)
1459 1459 continue
1460 1460 else:
1461 1461 mm.discard(x)
1462 1462 dd.add(x)
1463 1463
1464 1464 m = list(mm)
1465 1465 r = list(dd)
1466 1466 a = list(aa)
1467 1467 c = [filter(matchfn, l) for l in (m, a, r)]
1468 1468 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1469 1469 chunks = patchmod.diff(repo, patchparent, match=match,
1470 1470 changes=c, opts=diffopts)
1471 1471 for chunk in chunks:
1472 1472 patchf.write(chunk)
1473 1473
1474 1474 try:
1475 1475 if diffopts.git or diffopts.upgrade:
1476 1476 copies = {}
1477 1477 for dst in a:
1478 1478 src = repo.dirstate.copied(dst)
1479 1479 # during qfold, the source file for copies may
1480 1480 # be removed. Treat this as a simple add.
1481 1481 if src is not None and src in repo.dirstate:
1482 1482 copies.setdefault(src, []).append(dst)
1483 1483 repo.dirstate.add(dst)
1484 1484 # remember the copies between patchparent and qtip
1485 1485 for dst in aaa:
1486 1486 f = repo.file(dst)
1487 1487 src = f.renamed(man[dst])
1488 1488 if src:
1489 1489 copies.setdefault(src[0], []).extend(
1490 1490 copies.get(dst, []))
1491 1491 if dst in a:
1492 1492 copies[src[0]].append(dst)
1493 1493 # we can't copy a file created by the patch itself
1494 1494 if dst in copies:
1495 1495 del copies[dst]
1496 1496 for src, dsts in copies.iteritems():
1497 1497 for dst in dsts:
1498 1498 repo.dirstate.copy(src, dst)
1499 1499 else:
1500 1500 for dst in a:
1501 1501 repo.dirstate.add(dst)
1502 1502 # Drop useless copy information
1503 1503 for f in list(repo.dirstate.copies()):
1504 1504 repo.dirstate.copy(None, f)
1505 1505 for f in r:
1506 1506 repo.dirstate.remove(f)
1507 1507 # if the patch excludes a modified file, mark that
1508 1508 # file with mtime=0 so status can see it.
1509 1509 mm = []
1510 1510 for i in xrange(len(m)-1, -1, -1):
1511 1511 if not matchfn(m[i]):
1512 1512 mm.append(m[i])
1513 1513 del m[i]
1514 1514 for f in m:
1515 1515 repo.dirstate.normal(f)
1516 1516 for f in mm:
1517 1517 repo.dirstate.normallookup(f)
1518 1518 for f in forget:
1519 1519 repo.dirstate.drop(f)
1520 1520
1521 1521 if not msg:
1522 1522 if not ph.message:
1523 1523 message = "[mq]: %s\n" % patchfn
1524 1524 else:
1525 1525 message = "\n".join(ph.message)
1526 1526 else:
1527 1527 message = msg
1528 1528
1529 1529 user = ph.user or changes[1]
1530 1530
1531 1531 oldphase = repo[top].phase()
1532 1532
1533 1533 # assumes strip can roll itself back if interrupted
1534 1534 repo.dirstate.setparents(*cparents)
1535 1535 self.applied.pop()
1536 1536 self.applieddirty = True
1537 1537 self.strip(repo, [top], update=False,
1538 1538 backup='strip')
1539 1539 except:
1540 1540 repo.dirstate.invalidate()
1541 1541 raise
1542 1542
1543 1543 try:
1544 1544 # might be nice to attempt to roll back strip after this
1545 1545
1546 1546 # Ensure we create a new changeset in the same phase than
1547 1547 # the old one.
1548 1548 n = newcommit(repo, oldphase, message, user, ph.date,
1549 1549 match=match, force=True)
1550 1550 # only write patch after a successful commit
1551 1551 patchf.close()
1552 1552 self.applied.append(statusentry(n, patchfn))
1553 1553 except:
1554 1554 ctx = repo[cparents[0]]
1555 1555 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1556 1556 self.savedirty()
1557 1557 self.ui.warn(_('refresh interrupted while patch was popped! '
1558 1558 '(revert --all, qpush to recover)\n'))
1559 1559 raise
1560 1560 finally:
1561 1561 wlock.release()
1562 1562 self.removeundo(repo)
1563 1563
1564 1564 def init(self, repo, create=False):
1565 1565 if not create and os.path.isdir(self.path):
1566 1566 raise util.Abort(_("patch queue directory already exists"))
1567 1567 try:
1568 1568 os.mkdir(self.path)
1569 1569 except OSError, inst:
1570 1570 if inst.errno != errno.EEXIST or not create:
1571 1571 raise
1572 1572 if create:
1573 1573 return self.qrepo(create=True)
1574 1574
1575 1575 def unapplied(self, repo, patch=None):
1576 1576 if patch and patch not in self.series:
1577 1577 raise util.Abort(_("patch %s is not in series file") % patch)
1578 1578 if not patch:
1579 1579 start = self.seriesend()
1580 1580 else:
1581 1581 start = self.series.index(patch) + 1
1582 1582 unapplied = []
1583 1583 for i in xrange(start, len(self.series)):
1584 1584 pushable, reason = self.pushable(i)
1585 1585 if pushable:
1586 1586 unapplied.append((i, self.series[i]))
1587 1587 self.explainpushable(i)
1588 1588 return unapplied
1589 1589
1590 1590 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1591 1591 summary=False):
1592 1592 def displayname(pfx, patchname, state):
1593 1593 if pfx:
1594 1594 self.ui.write(pfx)
1595 1595 if summary:
1596 1596 ph = patchheader(self.join(patchname), self.plainmode)
1597 1597 msg = ph.message and ph.message[0] or ''
1598 1598 if self.ui.formatted():
1599 1599 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1600 1600 if width > 0:
1601 1601 msg = util.ellipsis(msg, width)
1602 1602 else:
1603 1603 msg = ''
1604 1604 self.ui.write(patchname, label='qseries.' + state)
1605 1605 self.ui.write(': ')
1606 1606 self.ui.write(msg, label='qseries.message.' + state)
1607 1607 else:
1608 1608 self.ui.write(patchname, label='qseries.' + state)
1609 1609 self.ui.write('\n')
1610 1610
1611 1611 applied = set([p.name for p in self.applied])
1612 1612 if length is None:
1613 1613 length = len(self.series) - start
1614 1614 if not missing:
1615 1615 if self.ui.verbose:
1616 1616 idxwidth = len(str(start + length - 1))
1617 1617 for i in xrange(start, start + length):
1618 1618 patch = self.series[i]
1619 1619 if patch in applied:
1620 1620 char, state = 'A', 'applied'
1621 1621 elif self.pushable(i)[0]:
1622 1622 char, state = 'U', 'unapplied'
1623 1623 else:
1624 1624 char, state = 'G', 'guarded'
1625 1625 pfx = ''
1626 1626 if self.ui.verbose:
1627 1627 pfx = '%*d %s ' % (idxwidth, i, char)
1628 1628 elif status and status != char:
1629 1629 continue
1630 1630 displayname(pfx, patch, state)
1631 1631 else:
1632 1632 msng_list = []
1633 1633 for root, dirs, files in os.walk(self.path):
1634 1634 d = root[len(self.path) + 1:]
1635 1635 for f in files:
1636 1636 fl = os.path.join(d, f)
1637 1637 if (fl not in self.series and
1638 1638 fl not in (self.statuspath, self.seriespath,
1639 1639 self.guardspath)
1640 1640 and not fl.startswith('.')):
1641 1641 msng_list.append(fl)
1642 1642 for x in sorted(msng_list):
1643 1643 pfx = self.ui.verbose and ('D ') or ''
1644 1644 displayname(pfx, x, 'missing')
1645 1645
1646 1646 def issaveline(self, l):
1647 1647 if l.name == '.hg.patches.save.line':
1648 1648 return True
1649 1649
1650 1650 def qrepo(self, create=False):
1651 1651 ui = self.ui.copy()
1652 1652 ui.setconfig('paths', 'default', '', overlay=False)
1653 1653 ui.setconfig('paths', 'default-push', '', overlay=False)
1654 1654 if create or os.path.isdir(self.join(".hg")):
1655 1655 return hg.repository(ui, path=self.path, create=create)
1656 1656
1657 1657 def restore(self, repo, rev, delete=None, qupdate=None):
1658 1658 desc = repo[rev].description().strip()
1659 1659 lines = desc.splitlines()
1660 1660 i = 0
1661 1661 datastart = None
1662 1662 series = []
1663 1663 applied = []
1664 1664 qpp = None
1665 1665 for i, line in enumerate(lines):
1666 1666 if line == 'Patch Data:':
1667 1667 datastart = i + 1
1668 1668 elif line.startswith('Dirstate:'):
1669 1669 l = line.rstrip()
1670 1670 l = l[10:].split(' ')
1671 1671 qpp = [bin(x) for x in l]
1672 1672 elif datastart is not None:
1673 1673 l = line.rstrip()
1674 1674 n, name = l.split(':', 1)
1675 1675 if n:
1676 1676 applied.append(statusentry(bin(n), name))
1677 1677 else:
1678 1678 series.append(l)
1679 1679 if datastart is None:
1680 1680 self.ui.warn(_("No saved patch data found\n"))
1681 1681 return 1
1682 1682 self.ui.warn(_("restoring status: %s\n") % lines[0])
1683 1683 self.fullseries = series
1684 1684 self.applied = applied
1685 1685 self.parseseries()
1686 1686 self.seriesdirty = True
1687 1687 self.applieddirty = True
1688 1688 heads = repo.changelog.heads()
1689 1689 if delete:
1690 1690 if rev not in heads:
1691 1691 self.ui.warn(_("save entry has children, leaving it alone\n"))
1692 1692 else:
1693 1693 self.ui.warn(_("removing save entry %s\n") % short(rev))
1694 1694 pp = repo.dirstate.parents()
1695 1695 if rev in pp:
1696 1696 update = True
1697 1697 else:
1698 1698 update = False
1699 1699 self.strip(repo, [rev], update=update, backup='strip')
1700 1700 if qpp:
1701 1701 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1702 1702 (short(qpp[0]), short(qpp[1])))
1703 1703 if qupdate:
1704 1704 self.ui.status(_("updating queue directory\n"))
1705 1705 r = self.qrepo()
1706 1706 if not r:
1707 1707 self.ui.warn(_("Unable to load queue repository\n"))
1708 1708 return 1
1709 1709 hg.clean(r, qpp[0])
1710 1710
1711 1711 def save(self, repo, msg=None):
1712 1712 if not self.applied:
1713 1713 self.ui.warn(_("save: no patches applied, exiting\n"))
1714 1714 return 1
1715 1715 if self.issaveline(self.applied[-1]):
1716 1716 self.ui.warn(_("status is already saved\n"))
1717 1717 return 1
1718 1718
1719 1719 if not msg:
1720 1720 msg = _("hg patches saved state")
1721 1721 else:
1722 1722 msg = "hg patches: " + msg.rstrip('\r\n')
1723 1723 r = self.qrepo()
1724 1724 if r:
1725 1725 pp = r.dirstate.parents()
1726 1726 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1727 1727 msg += "\n\nPatch Data:\n"
1728 1728 msg += ''.join('%s\n' % x for x in self.applied)
1729 1729 msg += ''.join(':%s\n' % x for x in self.fullseries)
1730 1730 n = repo.commit(msg, force=True)
1731 1731 if not n:
1732 1732 self.ui.warn(_("repo commit failed\n"))
1733 1733 return 1
1734 1734 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1735 1735 self.applieddirty = True
1736 1736 self.removeundo(repo)
1737 1737
1738 1738 def fullseriesend(self):
1739 1739 if self.applied:
1740 1740 p = self.applied[-1].name
1741 1741 end = self.findseries(p)
1742 1742 if end is None:
1743 1743 return len(self.fullseries)
1744 1744 return end + 1
1745 1745 return 0
1746 1746
1747 1747 def seriesend(self, all_patches=False):
1748 1748 """If all_patches is False, return the index of the next pushable patch
1749 1749 in the series, or the series length. If all_patches is True, return the
1750 1750 index of the first patch past the last applied one.
1751 1751 """
1752 1752 end = 0
1753 1753 def next(start):
1754 1754 if all_patches or start >= len(self.series):
1755 1755 return start
1756 1756 for i in xrange(start, len(self.series)):
1757 1757 p, reason = self.pushable(i)
1758 1758 if p:
1759 1759 return i
1760 1760 self.explainpushable(i)
1761 1761 return len(self.series)
1762 1762 if self.applied:
1763 1763 p = self.applied[-1].name
1764 1764 try:
1765 1765 end = self.series.index(p)
1766 1766 except ValueError:
1767 1767 return 0
1768 1768 return next(end + 1)
1769 1769 return next(end)
1770 1770
1771 1771 def appliedname(self, index):
1772 1772 pname = self.applied[index].name
1773 1773 if not self.ui.verbose:
1774 1774 p = pname
1775 1775 else:
1776 1776 p = str(self.series.index(pname)) + " " + pname
1777 1777 return p
1778 1778
1779 1779 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1780 1780 force=None, git=False):
1781 1781 def checkseries(patchname):
1782 1782 if patchname in self.series:
1783 1783 raise util.Abort(_('patch %s is already in the series file')
1784 1784 % patchname)
1785 1785
1786 1786 if rev:
1787 1787 if files:
1788 1788 raise util.Abort(_('option "-r" not valid when importing '
1789 1789 'files'))
1790 1790 rev = scmutil.revrange(repo, rev)
1791 1791 rev.sort(reverse=True)
1792 1792 if (len(files) > 1 or len(rev) > 1) and patchname:
1793 1793 raise util.Abort(_('option "-n" not valid when importing multiple '
1794 1794 'patches'))
1795 1795 imported = []
1796 1796 if rev:
1797 1797 # If mq patches are applied, we can only import revisions
1798 1798 # that form a linear path to qbase.
1799 1799 # Otherwise, they should form a linear path to a head.
1800 1800 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1801 1801 if len(heads) > 1:
1802 1802 raise util.Abort(_('revision %d is the root of more than one '
1803 1803 'branch') % rev[-1])
1804 1804 if self.applied:
1805 1805 base = repo.changelog.node(rev[0])
1806 1806 if base in [n.node for n in self.applied]:
1807 1807 raise util.Abort(_('revision %d is already managed')
1808 1808 % rev[0])
1809 1809 if heads != [self.applied[-1].node]:
1810 1810 raise util.Abort(_('revision %d is not the parent of '
1811 1811 'the queue') % rev[0])
1812 1812 base = repo.changelog.rev(self.applied[0].node)
1813 1813 lastparent = repo.changelog.parentrevs(base)[0]
1814 1814 else:
1815 1815 if heads != [repo.changelog.node(rev[0])]:
1816 1816 raise util.Abort(_('revision %d has unmanaged children')
1817 1817 % rev[0])
1818 1818 lastparent = None
1819 1819
1820 1820 diffopts = self.diffopts({'git': git})
1821 1821 for r in rev:
1822 1822 if not repo[r].mutable():
1823 1823 raise util.Abort(_('revision %d is not mutable') % r,
1824 1824 hint=_('see "hg help phases" for details'))
1825 1825 p1, p2 = repo.changelog.parentrevs(r)
1826 1826 n = repo.changelog.node(r)
1827 1827 if p2 != nullrev:
1828 1828 raise util.Abort(_('cannot import merge revision %d') % r)
1829 1829 if lastparent and lastparent != r:
1830 1830 raise util.Abort(_('revision %d is not the parent of %d')
1831 1831 % (r, lastparent))
1832 1832 lastparent = p1
1833 1833
1834 1834 if not patchname:
1835 1835 patchname = normname('%d.diff' % r)
1836 1836 checkseries(patchname)
1837 1837 self.checkpatchname(patchname, force)
1838 1838 self.fullseries.insert(0, patchname)
1839 1839
1840 1840 patchf = self.opener(patchname, "w")
1841 1841 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1842 1842 patchf.close()
1843 1843
1844 1844 se = statusentry(n, patchname)
1845 1845 self.applied.insert(0, se)
1846 1846
1847 1847 self.added.append(patchname)
1848 1848 imported.append(patchname)
1849 1849 patchname = None
1850 1850 if rev and repo.ui.configbool('mq', 'secret', False):
1851 1851 # if we added anything with --rev, we must move the secret root
1852 1852 phases.retractboundary(repo, phases.secret, [n])
1853 1853 self.parseseries()
1854 1854 self.applieddirty = True
1855 1855 self.seriesdirty = True
1856 1856
1857 1857 for i, filename in enumerate(files):
1858 1858 if existing:
1859 1859 if filename == '-':
1860 1860 raise util.Abort(_('-e is incompatible with import from -'))
1861 1861 filename = normname(filename)
1862 1862 self.checkreservedname(filename)
1863 1863 originpath = self.join(filename)
1864 1864 if not os.path.isfile(originpath):
1865 1865 raise util.Abort(_("patch %s does not exist") % filename)
1866 1866
1867 1867 if patchname:
1868 1868 self.checkpatchname(patchname, force)
1869 1869
1870 1870 self.ui.write(_('renaming %s to %s\n')
1871 1871 % (filename, patchname))
1872 1872 util.rename(originpath, self.join(patchname))
1873 1873 else:
1874 1874 patchname = filename
1875 1875
1876 1876 else:
1877 1877 if filename == '-' and not patchname:
1878 1878 raise util.Abort(_('need --name to import a patch from -'))
1879 1879 elif not patchname:
1880 1880 patchname = normname(os.path.basename(filename.rstrip('/')))
1881 1881 self.checkpatchname(patchname, force)
1882 1882 try:
1883 1883 if filename == '-':
1884 1884 text = self.ui.fin.read()
1885 1885 else:
1886 1886 fp = url.open(self.ui, filename)
1887 1887 text = fp.read()
1888 1888 fp.close()
1889 1889 except (OSError, IOError):
1890 1890 raise util.Abort(_("unable to read file %s") % filename)
1891 1891 patchf = self.opener(patchname, "w")
1892 1892 patchf.write(text)
1893 1893 patchf.close()
1894 1894 if not force:
1895 1895 checkseries(patchname)
1896 1896 if patchname not in self.series:
1897 1897 index = self.fullseriesend() + i
1898 1898 self.fullseries[index:index] = [patchname]
1899 1899 self.parseseries()
1900 1900 self.seriesdirty = True
1901 1901 self.ui.warn(_("adding %s to series file\n") % patchname)
1902 1902 self.added.append(patchname)
1903 1903 imported.append(patchname)
1904 1904 patchname = None
1905 1905
1906 1906 self.removeundo(repo)
1907 1907 return imported
1908 1908
1909 1909 @command("qdelete|qremove|qrm",
1910 1910 [('k', 'keep', None, _('keep patch file')),
1911 1911 ('r', 'rev', [],
1912 1912 _('stop managing a revision (DEPRECATED)'), _('REV'))],
1913 1913 _('hg qdelete [-k] [PATCH]...'))
1914 1914 def delete(ui, repo, *patches, **opts):
1915 1915 """remove patches from queue
1916 1916
1917 1917 The patches must not be applied, and at least one patch is required. Exact
1918 1918 patch identifiers must be given. With -k/--keep, the patch files are
1919 1919 preserved in the patch directory.
1920 1920
1921 1921 To stop managing a patch and move it into permanent history,
1922 1922 use the :hg:`qfinish` command."""
1923 1923 q = repo.mq
1924 1924 q.delete(repo, patches, opts)
1925 1925 q.savedirty()
1926 1926 return 0
1927 1927
1928 1928 @command("qapplied",
1929 1929 [('1', 'last', None, _('show only the preceding applied patch'))
1930 1930 ] + seriesopts,
1931 1931 _('hg qapplied [-1] [-s] [PATCH]'))
1932 1932 def applied(ui, repo, patch=None, **opts):
1933 1933 """print the patches already applied
1934 1934
1935 1935 Returns 0 on success."""
1936 1936
1937 1937 q = repo.mq
1938 1938
1939 1939 if patch:
1940 1940 if patch not in q.series:
1941 1941 raise util.Abort(_("patch %s is not in series file") % patch)
1942 1942 end = q.series.index(patch) + 1
1943 1943 else:
1944 1944 end = q.seriesend(True)
1945 1945
1946 1946 if opts.get('last') and not end:
1947 1947 ui.write(_("no patches applied\n"))
1948 1948 return 1
1949 1949 elif opts.get('last') and end == 1:
1950 1950 ui.write(_("only one patch applied\n"))
1951 1951 return 1
1952 1952 elif opts.get('last'):
1953 1953 start = end - 2
1954 1954 end = 1
1955 1955 else:
1956 1956 start = 0
1957 1957
1958 1958 q.qseries(repo, length=end, start=start, status='A',
1959 1959 summary=opts.get('summary'))
1960 1960
1961 1961
1962 1962 @command("qunapplied",
1963 1963 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
1964 1964 _('hg qunapplied [-1] [-s] [PATCH]'))
1965 1965 def unapplied(ui, repo, patch=None, **opts):
1966 1966 """print the patches not yet applied
1967 1967
1968 1968 Returns 0 on success."""
1969 1969
1970 1970 q = repo.mq
1971 1971 if patch:
1972 1972 if patch not in q.series:
1973 1973 raise util.Abort(_("patch %s is not in series file") % patch)
1974 1974 start = q.series.index(patch) + 1
1975 1975 else:
1976 1976 start = q.seriesend(True)
1977 1977
1978 1978 if start == len(q.series) and opts.get('first'):
1979 1979 ui.write(_("all patches applied\n"))
1980 1980 return 1
1981 1981
1982 1982 length = opts.get('first') and 1 or None
1983 1983 q.qseries(repo, start=start, length=length, status='U',
1984 1984 summary=opts.get('summary'))
1985 1985
1986 1986 @command("qimport",
1987 1987 [('e', 'existing', None, _('import file in patch directory')),
1988 1988 ('n', 'name', '',
1989 1989 _('name of patch file'), _('NAME')),
1990 1990 ('f', 'force', None, _('overwrite existing files')),
1991 1991 ('r', 'rev', [],
1992 1992 _('place existing revisions under mq control'), _('REV')),
1993 1993 ('g', 'git', None, _('use git extended diff format')),
1994 1994 ('P', 'push', None, _('qpush after importing'))],
1995 1995 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
1996 1996 def qimport(ui, repo, *filename, **opts):
1997 1997 """import a patch or existing changeset
1998 1998
1999 1999 The patch is inserted into the series after the last applied
2000 2000 patch. If no patches have been applied, qimport prepends the patch
2001 2001 to the series.
2002 2002
2003 2003 The patch will have the same name as its source file unless you
2004 2004 give it a new one with -n/--name.
2005 2005
2006 2006 You can register an existing patch inside the patch directory with
2007 2007 the -e/--existing flag.
2008 2008
2009 2009 With -f/--force, an existing patch of the same name will be
2010 2010 overwritten.
2011 2011
2012 2012 An existing changeset may be placed under mq control with -r/--rev
2013 2013 (e.g. qimport --rev tip -n patch will place tip under mq control).
2014 2014 With -g/--git, patches imported with --rev will use the git diff
2015 2015 format. See the diffs help topic for information on why this is
2016 2016 important for preserving rename/copy information and permission
2017 2017 changes. Use :hg:`qfinish` to remove changesets from mq control.
2018 2018
2019 2019 To import a patch from standard input, pass - as the patch file.
2020 2020 When importing from standard input, a patch name must be specified
2021 2021 using the --name flag.
2022 2022
2023 2023 To import an existing patch while renaming it::
2024 2024
2025 2025 hg qimport -e existing-patch -n new-name
2026 2026
2027 2027 Returns 0 if import succeeded.
2028 2028 """
2029 2029 lock = repo.lock() # cause this may move phase
2030 2030 try:
2031 2031 q = repo.mq
2032 2032 try:
2033 2033 imported = q.qimport(
2034 2034 repo, filename, patchname=opts.get('name'),
2035 2035 existing=opts.get('existing'), force=opts.get('force'),
2036 2036 rev=opts.get('rev'), git=opts.get('git'))
2037 2037 finally:
2038 2038 q.savedirty()
2039 2039
2040 2040
2041 2041 if imported and opts.get('push') and not opts.get('rev'):
2042 2042 return q.push(repo, imported[-1])
2043 2043 finally:
2044 2044 lock.release()
2045 2045 return 0
2046 2046
2047 2047 def qinit(ui, repo, create):
2048 2048 """initialize a new queue repository
2049 2049
2050 2050 This command also creates a series file for ordering patches, and
2051 2051 an mq-specific .hgignore file in the queue repository, to exclude
2052 2052 the status and guards files (these contain mostly transient state).
2053 2053
2054 2054 Returns 0 if initialization succeeded."""
2055 2055 q = repo.mq
2056 2056 r = q.init(repo, create)
2057 2057 q.savedirty()
2058 2058 if r:
2059 2059 if not os.path.exists(r.wjoin('.hgignore')):
2060 2060 fp = r.wopener('.hgignore', 'w')
2061 2061 fp.write('^\\.hg\n')
2062 2062 fp.write('^\\.mq\n')
2063 2063 fp.write('syntax: glob\n')
2064 2064 fp.write('status\n')
2065 2065 fp.write('guards\n')
2066 2066 fp.close()
2067 2067 if not os.path.exists(r.wjoin('series')):
2068 2068 r.wopener('series', 'w').close()
2069 2069 r[None].add(['.hgignore', 'series'])
2070 2070 commands.add(ui, r)
2071 2071 return 0
2072 2072
2073 2073 @command("^qinit",
2074 2074 [('c', 'create-repo', None, _('create queue repository'))],
2075 2075 _('hg qinit [-c]'))
2076 2076 def init(ui, repo, **opts):
2077 2077 """init a new queue repository (DEPRECATED)
2078 2078
2079 2079 The queue repository is unversioned by default. If
2080 2080 -c/--create-repo is specified, qinit will create a separate nested
2081 2081 repository for patches (qinit -c may also be run later to convert
2082 2082 an unversioned patch repository into a versioned one). You can use
2083 2083 qcommit to commit changes to this queue repository.
2084 2084
2085 2085 This command is deprecated. Without -c, it's implied by other relevant
2086 2086 commands. With -c, use :hg:`init --mq` instead."""
2087 2087 return qinit(ui, repo, create=opts.get('create_repo'))
2088 2088
2089 2089 @command("qclone",
2090 2090 [('', 'pull', None, _('use pull protocol to copy metadata')),
2091 2091 ('U', 'noupdate', None, _('do not update the new working directories')),
2092 2092 ('', 'uncompressed', None,
2093 2093 _('use uncompressed transfer (fast over LAN)')),
2094 2094 ('p', 'patches', '',
2095 2095 _('location of source patch repository'), _('REPO')),
2096 2096 ] + commands.remoteopts,
2097 2097 _('hg qclone [OPTION]... SOURCE [DEST]'))
2098 2098 def clone(ui, source, dest=None, **opts):
2099 2099 '''clone main and patch repository at same time
2100 2100
2101 2101 If source is local, destination will have no patches applied. If
2102 2102 source is remote, this command can not check if patches are
2103 2103 applied in source, so cannot guarantee that patches are not
2104 2104 applied in destination. If you clone remote repository, be sure
2105 2105 before that it has no patches applied.
2106 2106
2107 2107 Source patch repository is looked for in <src>/.hg/patches by
2108 2108 default. Use -p <url> to change.
2109 2109
2110 2110 The patch directory must be a nested Mercurial repository, as
2111 2111 would be created by :hg:`init --mq`.
2112 2112
2113 2113 Return 0 on success.
2114 2114 '''
2115 2115 def patchdir(repo):
2116 2116 """compute a patch repo url from a repo object"""
2117 2117 url = repo.url()
2118 2118 if url.endswith('/'):
2119 2119 url = url[:-1]
2120 2120 return url + '/.hg/patches'
2121 2121
2122 2122 # main repo (destination and sources)
2123 2123 if dest is None:
2124 2124 dest = hg.defaultdest(source)
2125 2125 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2126 2126
2127 2127 # patches repo (source only)
2128 2128 if opts.get('patches'):
2129 2129 patchespath = ui.expandpath(opts.get('patches'))
2130 2130 else:
2131 2131 patchespath = patchdir(sr)
2132 2132 try:
2133 2133 hg.repository(ui, patchespath)
2134 2134 except error.RepoError:
2135 2135 raise util.Abort(_('versioned patch repository not found'
2136 2136 ' (see init --mq)'))
2137 2137 qbase, destrev = None, None
2138 2138 if sr.local():
2139 2139 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2140 2140 qbase = sr.mq.applied[0].node
2141 2141 if not hg.islocal(dest):
2142 2142 heads = set(sr.heads())
2143 2143 destrev = list(heads.difference(sr.heads(qbase)))
2144 2144 destrev.append(sr.changelog.parents(qbase)[0])
2145 2145 elif sr.capable('lookup'):
2146 2146 try:
2147 2147 qbase = sr.lookup('qbase')
2148 2148 except error.RepoError:
2149 2149 pass
2150 2150
2151 2151 ui.note(_('cloning main repository\n'))
2152 2152 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2153 2153 pull=opts.get('pull'),
2154 2154 rev=destrev,
2155 2155 update=False,
2156 2156 stream=opts.get('uncompressed'))
2157 2157
2158 2158 ui.note(_('cloning patch repository\n'))
2159 2159 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2160 2160 pull=opts.get('pull'), update=not opts.get('noupdate'),
2161 2161 stream=opts.get('uncompressed'))
2162 2162
2163 2163 if dr.local():
2164 2164 if qbase:
2165 2165 ui.note(_('stripping applied patches from destination '
2166 2166 'repository\n'))
2167 2167 dr.mq.strip(dr, [qbase], update=False, backup=None)
2168 2168 if not opts.get('noupdate'):
2169 2169 ui.note(_('updating destination repository\n'))
2170 2170 hg.update(dr, dr.changelog.tip())
2171 2171
2172 2172 @command("qcommit|qci",
2173 2173 commands.table["^commit|ci"][1],
2174 2174 _('hg qcommit [OPTION]... [FILE]...'))
2175 2175 def commit(ui, repo, *pats, **opts):
2176 2176 """commit changes in the queue repository (DEPRECATED)
2177 2177
2178 2178 This command is deprecated; use :hg:`commit --mq` instead."""
2179 2179 q = repo.mq
2180 2180 r = q.qrepo()
2181 2181 if not r:
2182 2182 raise util.Abort('no queue repository')
2183 2183 commands.commit(r.ui, r, *pats, **opts)
2184 2184
2185 2185 @command("qseries",
2186 2186 [('m', 'missing', None, _('print patches not in series')),
2187 2187 ] + seriesopts,
2188 2188 _('hg qseries [-ms]'))
2189 2189 def series(ui, repo, **opts):
2190 2190 """print the entire series file
2191 2191
2192 2192 Returns 0 on success."""
2193 2193 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2194 2194 return 0
2195 2195
2196 2196 @command("qtop", seriesopts, _('hg qtop [-s]'))
2197 2197 def top(ui, repo, **opts):
2198 2198 """print the name of the current patch
2199 2199
2200 2200 Returns 0 on success."""
2201 2201 q = repo.mq
2202 2202 t = q.applied and q.seriesend(True) or 0
2203 2203 if t:
2204 2204 q.qseries(repo, start=t - 1, length=1, status='A',
2205 2205 summary=opts.get('summary'))
2206 2206 else:
2207 2207 ui.write(_("no patches applied\n"))
2208 2208 return 1
2209 2209
2210 2210 @command("qnext", seriesopts, _('hg qnext [-s]'))
2211 2211 def next(ui, repo, **opts):
2212 2212 """print the name of the next pushable patch
2213 2213
2214 2214 Returns 0 on success."""
2215 2215 q = repo.mq
2216 2216 end = q.seriesend()
2217 2217 if end == len(q.series):
2218 2218 ui.write(_("all patches applied\n"))
2219 2219 return 1
2220 2220 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2221 2221
2222 2222 @command("qprev", seriesopts, _('hg qprev [-s]'))
2223 2223 def prev(ui, repo, **opts):
2224 2224 """print the name of the preceding applied patch
2225 2225
2226 2226 Returns 0 on success."""
2227 2227 q = repo.mq
2228 2228 l = len(q.applied)
2229 2229 if l == 1:
2230 2230 ui.write(_("only one patch applied\n"))
2231 2231 return 1
2232 2232 if not l:
2233 2233 ui.write(_("no patches applied\n"))
2234 2234 return 1
2235 2235 idx = q.series.index(q.applied[-2].name)
2236 2236 q.qseries(repo, start=idx, length=1, status='A',
2237 2237 summary=opts.get('summary'))
2238 2238
2239 2239 def setupheaderopts(ui, opts):
2240 2240 if not opts.get('user') and opts.get('currentuser'):
2241 2241 opts['user'] = ui.username()
2242 2242 if not opts.get('date') and opts.get('currentdate'):
2243 2243 opts['date'] = "%d %d" % util.makedate()
2244 2244
2245 2245 @command("^qnew",
2246 2246 [('e', 'edit', None, _('edit commit message')),
2247 2247 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2248 2248 ('g', 'git', None, _('use git extended diff format')),
2249 2249 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2250 2250 ('u', 'user', '',
2251 2251 _('add "From: <USER>" to patch'), _('USER')),
2252 2252 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2253 2253 ('d', 'date', '',
2254 2254 _('add "Date: <DATE>" to patch'), _('DATE'))
2255 2255 ] + commands.walkopts + commands.commitopts,
2256 2256 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2257 2257 def new(ui, repo, patch, *args, **opts):
2258 2258 """create a new patch
2259 2259
2260 2260 qnew creates a new patch on top of the currently-applied patch (if
2261 2261 any). The patch will be initialized with any outstanding changes
2262 2262 in the working directory. You may also use -I/--include,
2263 2263 -X/--exclude, and/or a list of files after the patch name to add
2264 2264 only changes to matching files to the new patch, leaving the rest
2265 2265 as uncommitted modifications.
2266 2266
2267 2267 -u/--user and -d/--date can be used to set the (given) user and
2268 2268 date, respectively. -U/--currentuser and -D/--currentdate set user
2269 2269 to current user and date to current date.
2270 2270
2271 2271 -e/--edit, -m/--message or -l/--logfile set the patch header as
2272 2272 well as the commit message. If none is specified, the header is
2273 2273 empty and the commit message is '[mq]: PATCH'.
2274 2274
2275 2275 Use the -g/--git option to keep the patch in the git extended diff
2276 2276 format. Read the diffs help topic for more information on why this
2277 2277 is important for preserving permission changes and copy/rename
2278 2278 information.
2279 2279
2280 2280 Returns 0 on successful creation of a new patch.
2281 2281 """
2282 2282 msg = cmdutil.logmessage(ui, opts)
2283 2283 def getmsg():
2284 2284 return ui.edit(msg, opts.get('user') or ui.username())
2285 2285 q = repo.mq
2286 2286 opts['msg'] = msg
2287 2287 if opts.get('edit'):
2288 2288 opts['msg'] = getmsg
2289 2289 else:
2290 2290 opts['msg'] = msg
2291 2291 setupheaderopts(ui, opts)
2292 2292 q.new(repo, patch, *args, **opts)
2293 2293 q.savedirty()
2294 2294 return 0
2295 2295
2296 2296 @command("^qrefresh",
2297 2297 [('e', 'edit', None, _('edit commit message')),
2298 2298 ('g', 'git', None, _('use git extended diff format')),
2299 2299 ('s', 'short', None,
2300 2300 _('refresh only files already in the patch and specified files')),
2301 2301 ('U', 'currentuser', None,
2302 2302 _('add/update author field in patch with current user')),
2303 2303 ('u', 'user', '',
2304 2304 _('add/update author field in patch with given user'), _('USER')),
2305 2305 ('D', 'currentdate', None,
2306 2306 _('add/update date field in patch with current date')),
2307 2307 ('d', 'date', '',
2308 2308 _('add/update date field in patch with given date'), _('DATE'))
2309 2309 ] + commands.walkopts + commands.commitopts,
2310 2310 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2311 2311 def refresh(ui, repo, *pats, **opts):
2312 2312 """update the current patch
2313 2313
2314 2314 If any file patterns are provided, the refreshed patch will
2315 2315 contain only the modifications that match those patterns; the
2316 2316 remaining modifications will remain in the working directory.
2317 2317
2318 2318 If -s/--short is specified, files currently included in the patch
2319 2319 will be refreshed just like matched files and remain in the patch.
2320 2320
2321 2321 If -e/--edit is specified, Mercurial will start your configured editor for
2322 2322 you to enter a message. In case qrefresh fails, you will find a backup of
2323 2323 your message in ``.hg/last-message.txt``.
2324 2324
2325 2325 hg add/remove/copy/rename work as usual, though you might want to
2326 2326 use git-style patches (-g/--git or [diff] git=1) to track copies
2327 2327 and renames. See the diffs help topic for more information on the
2328 2328 git diff format.
2329 2329
2330 2330 Returns 0 on success.
2331 2331 """
2332 2332 q = repo.mq
2333 2333 message = cmdutil.logmessage(ui, opts)
2334 2334 if opts.get('edit'):
2335 2335 if not q.applied:
2336 2336 ui.write(_("no patches applied\n"))
2337 2337 return 1
2338 2338 if message:
2339 2339 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2340 2340 patch = q.applied[-1].name
2341 2341 ph = patchheader(q.join(patch), q.plainmode)
2342 2342 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2343 2343 # We don't want to lose the patch message if qrefresh fails (issue2062)
2344 2344 repo.savecommitmessage(message)
2345 2345 setupheaderopts(ui, opts)
2346 2346 wlock = repo.wlock()
2347 2347 try:
2348 2348 ret = q.refresh(repo, pats, msg=message, **opts)
2349 2349 q.savedirty()
2350 2350 return ret
2351 2351 finally:
2352 2352 wlock.release()
2353 2353
2354 2354 @command("^qdiff",
2355 2355 commands.diffopts + commands.diffopts2 + commands.walkopts,
2356 2356 _('hg qdiff [OPTION]... [FILE]...'))
2357 2357 def diff(ui, repo, *pats, **opts):
2358 2358 """diff of the current patch and subsequent modifications
2359 2359
2360 2360 Shows a diff which includes the current patch as well as any
2361 2361 changes which have been made in the working directory since the
2362 2362 last refresh (thus showing what the current patch would become
2363 2363 after a qrefresh).
2364 2364
2365 2365 Use :hg:`diff` if you only want to see the changes made since the
2366 2366 last qrefresh, or :hg:`export qtip` if you want to see changes
2367 2367 made by the current patch without including changes made since the
2368 2368 qrefresh.
2369 2369
2370 2370 Returns 0 on success.
2371 2371 """
2372 2372 repo.mq.diff(repo, pats, opts)
2373 2373 return 0
2374 2374
2375 2375 @command('qfold',
2376 2376 [('e', 'edit', None, _('edit patch header')),
2377 2377 ('k', 'keep', None, _('keep folded patch files')),
2378 2378 ] + commands.commitopts,
2379 2379 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2380 2380 def fold(ui, repo, *files, **opts):
2381 2381 """fold the named patches into the current patch
2382 2382
2383 2383 Patches must not yet be applied. Each patch will be successively
2384 2384 applied to the current patch in the order given. If all the
2385 2385 patches apply successfully, the current patch will be refreshed
2386 2386 with the new cumulative patch, and the folded patches will be
2387 2387 deleted. With -k/--keep, the folded patch files will not be
2388 2388 removed afterwards.
2389 2389
2390 2390 The header for each folded patch will be concatenated with the
2391 2391 current patch header, separated by a line of ``* * *``.
2392 2392
2393 2393 Returns 0 on success."""
2394 2394 q = repo.mq
2395 2395 if not files:
2396 2396 raise util.Abort(_('qfold requires at least one patch name'))
2397 2397 if not q.checktoppatch(repo)[0]:
2398 2398 raise util.Abort(_('no patches applied'))
2399 2399 q.checklocalchanges(repo)
2400 2400
2401 2401 message = cmdutil.logmessage(ui, opts)
2402 2402 if opts.get('edit'):
2403 2403 if message:
2404 2404 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2405 2405
2406 2406 parent = q.lookup('qtip')
2407 2407 patches = []
2408 2408 messages = []
2409 2409 for f in files:
2410 2410 p = q.lookup(f)
2411 2411 if p in patches or p == parent:
2412 2412 ui.warn(_('Skipping already folded patch %s\n') % p)
2413 2413 if q.isapplied(p):
2414 2414 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2415 2415 patches.append(p)
2416 2416
2417 2417 for p in patches:
2418 2418 if not message:
2419 2419 ph = patchheader(q.join(p), q.plainmode)
2420 2420 if ph.message:
2421 2421 messages.append(ph.message)
2422 2422 pf = q.join(p)
2423 2423 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2424 2424 if not patchsuccess:
2425 2425 raise util.Abort(_('error folding patch %s') % p)
2426 2426
2427 2427 if not message:
2428 2428 ph = patchheader(q.join(parent), q.plainmode)
2429 2429 message, user = ph.message, ph.user
2430 2430 for msg in messages:
2431 2431 message.append('* * *')
2432 2432 message.extend(msg)
2433 2433 message = '\n'.join(message)
2434 2434
2435 2435 if opts.get('edit'):
2436 2436 message = ui.edit(message, user or ui.username())
2437 2437
2438 2438 diffopts = q.patchopts(q.diffopts(), *patches)
2439 2439 wlock = repo.wlock()
2440 2440 try:
2441 2441 q.refresh(repo, msg=message, git=diffopts.git)
2442 2442 q.delete(repo, patches, opts)
2443 2443 q.savedirty()
2444 2444 finally:
2445 2445 wlock.release()
2446 2446
2447 2447 @command("qgoto",
2448 2448 [('f', 'force', None, _('overwrite any local changes'))],
2449 2449 _('hg qgoto [OPTION]... PATCH'))
2450 2450 def goto(ui, repo, patch, **opts):
2451 2451 '''push or pop patches until named patch is at top of stack
2452 2452
2453 2453 Returns 0 on success.'''
2454 2454 q = repo.mq
2455 2455 patch = q.lookup(patch)
2456 2456 if q.isapplied(patch):
2457 2457 ret = q.pop(repo, patch, force=opts.get('force'))
2458 2458 else:
2459 2459 ret = q.push(repo, patch, force=opts.get('force'))
2460 2460 q.savedirty()
2461 2461 return ret
2462 2462
2463 2463 @command("qguard",
2464 2464 [('l', 'list', None, _('list all patches and guards')),
2465 2465 ('n', 'none', None, _('drop all guards'))],
2466 2466 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2467 2467 def guard(ui, repo, *args, **opts):
2468 2468 '''set or print guards for a patch
2469 2469
2470 2470 Guards control whether a patch can be pushed. A patch with no
2471 2471 guards is always pushed. A patch with a positive guard ("+foo") is
2472 2472 pushed only if the :hg:`qselect` command has activated it. A patch with
2473 2473 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2474 2474 has activated it.
2475 2475
2476 2476 With no arguments, print the currently active guards.
2477 2477 With arguments, set guards for the named patch.
2478 2478
2479 2479 .. note::
2480 2480 Specifying negative guards now requires '--'.
2481 2481
2482 2482 To set guards on another patch::
2483 2483
2484 2484 hg qguard other.patch -- +2.6.17 -stable
2485 2485
2486 2486 Returns 0 on success.
2487 2487 '''
2488 2488 def status(idx):
2489 2489 guards = q.seriesguards[idx] or ['unguarded']
2490 2490 if q.series[idx] in applied:
2491 2491 state = 'applied'
2492 2492 elif q.pushable(idx)[0]:
2493 2493 state = 'unapplied'
2494 2494 else:
2495 2495 state = 'guarded'
2496 2496 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2497 2497 ui.write('%s: ' % ui.label(q.series[idx], label))
2498 2498
2499 2499 for i, guard in enumerate(guards):
2500 2500 if guard.startswith('+'):
2501 2501 ui.write(guard, label='qguard.positive')
2502 2502 elif guard.startswith('-'):
2503 2503 ui.write(guard, label='qguard.negative')
2504 2504 else:
2505 2505 ui.write(guard, label='qguard.unguarded')
2506 2506 if i != len(guards) - 1:
2507 2507 ui.write(' ')
2508 2508 ui.write('\n')
2509 2509 q = repo.mq
2510 2510 applied = set(p.name for p in q.applied)
2511 2511 patch = None
2512 2512 args = list(args)
2513 2513 if opts.get('list'):
2514 2514 if args or opts.get('none'):
2515 2515 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2516 2516 for i in xrange(len(q.series)):
2517 2517 status(i)
2518 2518 return
2519 2519 if not args or args[0][0:1] in '-+':
2520 2520 if not q.applied:
2521 2521 raise util.Abort(_('no patches applied'))
2522 2522 patch = q.applied[-1].name
2523 2523 if patch is None and args[0][0:1] not in '-+':
2524 2524 patch = args.pop(0)
2525 2525 if patch is None:
2526 2526 raise util.Abort(_('no patch to work with'))
2527 2527 if args or opts.get('none'):
2528 2528 idx = q.findseries(patch)
2529 2529 if idx is None:
2530 2530 raise util.Abort(_('no patch named %s') % patch)
2531 2531 q.setguards(idx, args)
2532 2532 q.savedirty()
2533 2533 else:
2534 2534 status(q.series.index(q.lookup(patch)))
2535 2535
2536 2536 @command("qheader", [], _('hg qheader [PATCH]'))
2537 2537 def header(ui, repo, patch=None):
2538 2538 """print the header of the topmost or specified patch
2539 2539
2540 2540 Returns 0 on success."""
2541 2541 q = repo.mq
2542 2542
2543 2543 if patch:
2544 2544 patch = q.lookup(patch)
2545 2545 else:
2546 2546 if not q.applied:
2547 2547 ui.write(_('no patches applied\n'))
2548 2548 return 1
2549 2549 patch = q.lookup('qtip')
2550 2550 ph = patchheader(q.join(patch), q.plainmode)
2551 2551
2552 2552 ui.write('\n'.join(ph.message) + '\n')
2553 2553
2554 2554 def lastsavename(path):
2555 2555 (directory, base) = os.path.split(path)
2556 2556 names = os.listdir(directory)
2557 2557 namere = re.compile("%s.([0-9]+)" % base)
2558 2558 maxindex = None
2559 2559 maxname = None
2560 2560 for f in names:
2561 2561 m = namere.match(f)
2562 2562 if m:
2563 2563 index = int(m.group(1))
2564 2564 if maxindex is None or index > maxindex:
2565 2565 maxindex = index
2566 2566 maxname = f
2567 2567 if maxname:
2568 2568 return (os.path.join(directory, maxname), maxindex)
2569 2569 return (None, None)
2570 2570
2571 2571 def savename(path):
2572 2572 (last, index) = lastsavename(path)
2573 2573 if last is None:
2574 2574 index = 0
2575 2575 newpath = path + ".%d" % (index + 1)
2576 2576 return newpath
2577 2577
2578 2578 @command("^qpush",
2579 2579 [('f', 'force', None, _('apply on top of local changes')),
2580 2580 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
2581 2581 ('l', 'list', None, _('list patch name in commit text')),
2582 2582 ('a', 'all', None, _('apply all patches')),
2583 2583 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2584 2584 ('n', 'name', '',
2585 2585 _('merge queue name (DEPRECATED)'), _('NAME')),
2586 2586 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2587 2587 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2588 2588 def push(ui, repo, patch=None, **opts):
2589 2589 """push the next patch onto the stack
2590 2590
2591 2591 When -f/--force is applied, all local changes in patched files
2592 2592 will be lost.
2593 2593
2594 2594 Return 0 on success.
2595 2595 """
2596 2596 q = repo.mq
2597 2597 mergeq = None
2598 2598
2599 2599 if opts.get('merge'):
2600 2600 if opts.get('name'):
2601 2601 newpath = repo.join(opts.get('name'))
2602 2602 else:
2603 2603 newpath, i = lastsavename(q.path)
2604 2604 if not newpath:
2605 2605 ui.warn(_("no saved queues found, please use -n\n"))
2606 2606 return 1
2607 2607 mergeq = queue(ui, repo.path, newpath)
2608 2608 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2609 2609 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2610 2610 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2611 2611 exact=opts.get('exact'))
2612 2612 return ret
2613 2613
2614 2614 @command("^qpop",
2615 2615 [('a', 'all', None, _('pop all patches')),
2616 2616 ('n', 'name', '',
2617 2617 _('queue name to pop (DEPRECATED)'), _('NAME')),
2618 2618 ('f', 'force', None, _('forget any local changes to patched files'))],
2619 2619 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2620 2620 def pop(ui, repo, patch=None, **opts):
2621 2621 """pop the current patch off the stack
2622 2622
2623 2623 By default, pops off the top of the patch stack. If given a patch
2624 2624 name, keeps popping off patches until the named patch is at the
2625 2625 top of the stack.
2626 2626
2627 2627 Return 0 on success.
2628 2628 """
2629 2629 localupdate = True
2630 2630 if opts.get('name'):
2631 2631 q = queue(ui, repo.path, repo.join(opts.get('name')))
2632 2632 ui.warn(_('using patch queue: %s\n') % q.path)
2633 2633 localupdate = False
2634 2634 else:
2635 2635 q = repo.mq
2636 2636 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2637 2637 all=opts.get('all'))
2638 2638 q.savedirty()
2639 2639 return ret
2640 2640
2641 2641 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2642 2642 def rename(ui, repo, patch, name=None, **opts):
2643 2643 """rename a patch
2644 2644
2645 2645 With one argument, renames the current patch to PATCH1.
2646 2646 With two arguments, renames PATCH1 to PATCH2.
2647 2647
2648 2648 Returns 0 on success."""
2649 2649 q = repo.mq
2650 2650 if not name:
2651 2651 name = patch
2652 2652 patch = None
2653 2653
2654 2654 if patch:
2655 2655 patch = q.lookup(patch)
2656 2656 else:
2657 2657 if not q.applied:
2658 2658 ui.write(_('no patches applied\n'))
2659 2659 return
2660 2660 patch = q.lookup('qtip')
2661 2661 absdest = q.join(name)
2662 2662 if os.path.isdir(absdest):
2663 2663 name = normname(os.path.join(name, os.path.basename(patch)))
2664 2664 absdest = q.join(name)
2665 2665 q.checkpatchname(name)
2666 2666
2667 2667 ui.note(_('renaming %s to %s\n') % (patch, name))
2668 2668 i = q.findseries(patch)
2669 2669 guards = q.guard_re.findall(q.fullseries[i])
2670 2670 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2671 2671 q.parseseries()
2672 2672 q.seriesdirty = True
2673 2673
2674 2674 info = q.isapplied(patch)
2675 2675 if info:
2676 2676 q.applied[info[0]] = statusentry(info[1], name)
2677 2677 q.applieddirty = True
2678 2678
2679 2679 destdir = os.path.dirname(absdest)
2680 2680 if not os.path.isdir(destdir):
2681 2681 os.makedirs(destdir)
2682 2682 util.rename(q.join(patch), absdest)
2683 2683 r = q.qrepo()
2684 2684 if r and patch in r.dirstate:
2685 2685 wctx = r[None]
2686 2686 wlock = r.wlock()
2687 2687 try:
2688 2688 if r.dirstate[patch] == 'a':
2689 2689 r.dirstate.drop(patch)
2690 2690 r.dirstate.add(name)
2691 2691 else:
2692 2692 wctx.copy(patch, name)
2693 2693 wctx.forget([patch])
2694 2694 finally:
2695 2695 wlock.release()
2696 2696
2697 2697 q.savedirty()
2698 2698
2699 2699 @command("qrestore",
2700 2700 [('d', 'delete', None, _('delete save entry')),
2701 2701 ('u', 'update', None, _('update queue working directory'))],
2702 2702 _('hg qrestore [-d] [-u] REV'))
2703 2703 def restore(ui, repo, rev, **opts):
2704 2704 """restore the queue state saved by a revision (DEPRECATED)
2705 2705
2706 2706 This command is deprecated, use :hg:`rebase` instead."""
2707 2707 rev = repo.lookup(rev)
2708 2708 q = repo.mq
2709 2709 q.restore(repo, rev, delete=opts.get('delete'),
2710 2710 qupdate=opts.get('update'))
2711 2711 q.savedirty()
2712 2712 return 0
2713 2713
2714 2714 @command("qsave",
2715 2715 [('c', 'copy', None, _('copy patch directory')),
2716 2716 ('n', 'name', '',
2717 2717 _('copy directory name'), _('NAME')),
2718 2718 ('e', 'empty', None, _('clear queue status file')),
2719 2719 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2720 2720 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2721 2721 def save(ui, repo, **opts):
2722 2722 """save current queue state (DEPRECATED)
2723 2723
2724 2724 This command is deprecated, use :hg:`rebase` instead."""
2725 2725 q = repo.mq
2726 2726 message = cmdutil.logmessage(ui, opts)
2727 2727 ret = q.save(repo, msg=message)
2728 2728 if ret:
2729 2729 return ret
2730 2730 q.savedirty() # save to .hg/patches before copying
2731 2731 if opts.get('copy'):
2732 2732 path = q.path
2733 2733 if opts.get('name'):
2734 2734 newpath = os.path.join(q.basepath, opts.get('name'))
2735 2735 if os.path.exists(newpath):
2736 2736 if not os.path.isdir(newpath):
2737 2737 raise util.Abort(_('destination %s exists and is not '
2738 2738 'a directory') % newpath)
2739 2739 if not opts.get('force'):
2740 2740 raise util.Abort(_('destination %s exists, '
2741 2741 'use -f to force') % newpath)
2742 2742 else:
2743 2743 newpath = savename(path)
2744 2744 ui.warn(_("copy %s to %s\n") % (path, newpath))
2745 2745 util.copyfiles(path, newpath)
2746 2746 if opts.get('empty'):
2747 2747 del q.applied[:]
2748 2748 q.applieddirty = True
2749 2749 q.savedirty()
2750 2750 return 0
2751 2751
2752 2752 @command("strip",
2753 2753 [
2754 2754 ('r', 'rev', [], _('strip specified revision (optional, '
2755 2755 'can specify revisions without this '
2756 2756 'option)'), _('REV')),
2757 2757 ('f', 'force', None, _('force removal of changesets, discard '
2758 2758 'uncommitted changes (no backup)')),
2759 2759 ('b', 'backup', None, _('bundle only changesets with local revision'
2760 2760 ' number greater than REV which are not'
2761 2761 ' descendants of REV (DEPRECATED)')),
2762 ('n', 'no-backup', None, _('no backups')),
2762 ('', 'no-backup', None, _('no backups')),
2763 2763 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2764 ('n', '', None, _('ignored (DEPRECATED)')),
2764 2765 ('k', 'keep', None, _("do not modify working copy during strip"))],
2765 2766 _('hg strip [-k] [-f] [-n] REV...'))
2766 2767 def strip(ui, repo, *revs, **opts):
2767 2768 """strip changesets and all their descendants from the repository
2768 2769
2769 2770 The strip command removes the specified changesets and all their
2770 2771 descendants. If the working directory has uncommitted changes, the
2771 2772 operation is aborted unless the --force flag is supplied, in which
2772 2773 case changes will be discarded.
2773 2774
2774 2775 If a parent of the working directory is stripped, then the working
2775 2776 directory will automatically be updated to the most recent
2776 2777 available ancestor of the stripped parent after the operation
2777 2778 completes.
2778 2779
2779 2780 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2780 2781 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2781 2782 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2782 2783 where BUNDLE is the bundle file created by the strip. Note that
2783 2784 the local revision numbers will in general be different after the
2784 2785 restore.
2785 2786
2786 2787 Use the --no-backup option to discard the backup bundle once the
2787 2788 operation completes.
2788 2789
2789 2790 Return 0 on success.
2790 2791 """
2791 2792 backup = 'all'
2792 2793 if opts.get('backup'):
2793 2794 backup = 'strip'
2794 2795 elif opts.get('no_backup') or opts.get('nobackup'):
2795 2796 backup = 'none'
2796 2797
2797 2798 cl = repo.changelog
2798 2799 revs = list(revs) + opts.get('rev')
2799 2800 revs = set(scmutil.revrange(repo, revs))
2800 2801 if not revs:
2801 2802 raise util.Abort(_('empty revision set'))
2802 2803
2803 2804 descendants = set(cl.descendants(*revs))
2804 2805 strippedrevs = revs.union(descendants)
2805 2806 roots = revs.difference(descendants)
2806 2807
2807 2808 update = False
2808 2809 # if one of the wdir parent is stripped we'll need
2809 2810 # to update away to an earlier revision
2810 2811 for p in repo.dirstate.parents():
2811 2812 if p != nullid and cl.rev(p) in strippedrevs:
2812 2813 update = True
2813 2814 break
2814 2815
2815 2816 rootnodes = set(cl.node(r) for r in roots)
2816 2817
2817 2818 q = repo.mq
2818 2819 if q.applied:
2819 2820 # refresh queue state if we're about to strip
2820 2821 # applied patches
2821 2822 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2822 2823 q.applieddirty = True
2823 2824 start = 0
2824 2825 end = len(q.applied)
2825 2826 for i, statusentry in enumerate(q.applied):
2826 2827 if statusentry.node in rootnodes:
2827 2828 # if one of the stripped roots is an applied
2828 2829 # patch, only part of the queue is stripped
2829 2830 start = i
2830 2831 break
2831 2832 del q.applied[start:end]
2832 2833 q.savedirty()
2833 2834
2834 2835 revs = list(rootnodes)
2835 2836 if update and opts.get('keep'):
2836 2837 wlock = repo.wlock()
2837 2838 try:
2838 2839 urev = repo.mq.qparents(repo, revs[0])
2839 2840 repo.dirstate.rebuild(urev, repo[urev].manifest())
2840 2841 repo.dirstate.write()
2841 2842 update = False
2842 2843 finally:
2843 2844 wlock.release()
2844 2845
2845 2846 repo.mq.strip(repo, revs, backup=backup, update=update,
2846 2847 force=opts.get('force'))
2847 2848 return 0
2848 2849
2849 2850 @command("qselect",
2850 2851 [('n', 'none', None, _('disable all guards')),
2851 2852 ('s', 'series', None, _('list all guards in series file')),
2852 2853 ('', 'pop', None, _('pop to before first guarded applied patch')),
2853 2854 ('', 'reapply', None, _('pop, then reapply patches'))],
2854 2855 _('hg qselect [OPTION]... [GUARD]...'))
2855 2856 def select(ui, repo, *args, **opts):
2856 2857 '''set or print guarded patches to push
2857 2858
2858 2859 Use the :hg:`qguard` command to set or print guards on patch, then use
2859 2860 qselect to tell mq which guards to use. A patch will be pushed if
2860 2861 it has no guards or any positive guards match the currently
2861 2862 selected guard, but will not be pushed if any negative guards
2862 2863 match the current guard. For example::
2863 2864
2864 2865 qguard foo.patch -- -stable (negative guard)
2865 2866 qguard bar.patch +stable (positive guard)
2866 2867 qselect stable
2867 2868
2868 2869 This activates the "stable" guard. mq will skip foo.patch (because
2869 2870 it has a negative match) but push bar.patch (because it has a
2870 2871 positive match).
2871 2872
2872 2873 With no arguments, prints the currently active guards.
2873 2874 With one argument, sets the active guard.
2874 2875
2875 2876 Use -n/--none to deactivate guards (no other arguments needed).
2876 2877 When no guards are active, patches with positive guards are
2877 2878 skipped and patches with negative guards are pushed.
2878 2879
2879 2880 qselect can change the guards on applied patches. It does not pop
2880 2881 guarded patches by default. Use --pop to pop back to the last
2881 2882 applied patch that is not guarded. Use --reapply (which implies
2882 2883 --pop) to push back to the current patch afterwards, but skip
2883 2884 guarded patches.
2884 2885
2885 2886 Use -s/--series to print a list of all guards in the series file
2886 2887 (no other arguments needed). Use -v for more information.
2887 2888
2888 2889 Returns 0 on success.'''
2889 2890
2890 2891 q = repo.mq
2891 2892 guards = q.active()
2892 2893 if args or opts.get('none'):
2893 2894 old_unapplied = q.unapplied(repo)
2894 2895 old_guarded = [i for i in xrange(len(q.applied)) if
2895 2896 not q.pushable(i)[0]]
2896 2897 q.setactive(args)
2897 2898 q.savedirty()
2898 2899 if not args:
2899 2900 ui.status(_('guards deactivated\n'))
2900 2901 if not opts.get('pop') and not opts.get('reapply'):
2901 2902 unapplied = q.unapplied(repo)
2902 2903 guarded = [i for i in xrange(len(q.applied))
2903 2904 if not q.pushable(i)[0]]
2904 2905 if len(unapplied) != len(old_unapplied):
2905 2906 ui.status(_('number of unguarded, unapplied patches has '
2906 2907 'changed from %d to %d\n') %
2907 2908 (len(old_unapplied), len(unapplied)))
2908 2909 if len(guarded) != len(old_guarded):
2909 2910 ui.status(_('number of guarded, applied patches has changed '
2910 2911 'from %d to %d\n') %
2911 2912 (len(old_guarded), len(guarded)))
2912 2913 elif opts.get('series'):
2913 2914 guards = {}
2914 2915 noguards = 0
2915 2916 for gs in q.seriesguards:
2916 2917 if not gs:
2917 2918 noguards += 1
2918 2919 for g in gs:
2919 2920 guards.setdefault(g, 0)
2920 2921 guards[g] += 1
2921 2922 if ui.verbose:
2922 2923 guards['NONE'] = noguards
2923 2924 guards = guards.items()
2924 2925 guards.sort(key=lambda x: x[0][1:])
2925 2926 if guards:
2926 2927 ui.note(_('guards in series file:\n'))
2927 2928 for guard, count in guards:
2928 2929 ui.note('%2d ' % count)
2929 2930 ui.write(guard, '\n')
2930 2931 else:
2931 2932 ui.note(_('no guards in series file\n'))
2932 2933 else:
2933 2934 if guards:
2934 2935 ui.note(_('active guards:\n'))
2935 2936 for g in guards:
2936 2937 ui.write(g, '\n')
2937 2938 else:
2938 2939 ui.write(_('no active guards\n'))
2939 2940 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2940 2941 popped = False
2941 2942 if opts.get('pop') or opts.get('reapply'):
2942 2943 for i in xrange(len(q.applied)):
2943 2944 pushable, reason = q.pushable(i)
2944 2945 if not pushable:
2945 2946 ui.status(_('popping guarded patches\n'))
2946 2947 popped = True
2947 2948 if i == 0:
2948 2949 q.pop(repo, all=True)
2949 2950 else:
2950 2951 q.pop(repo, str(i - 1))
2951 2952 break
2952 2953 if popped:
2953 2954 try:
2954 2955 if reapply:
2955 2956 ui.status(_('reapplying unguarded patches\n'))
2956 2957 q.push(repo, reapply)
2957 2958 finally:
2958 2959 q.savedirty()
2959 2960
2960 2961 @command("qfinish",
2961 2962 [('a', 'applied', None, _('finish all applied changesets'))],
2962 2963 _('hg qfinish [-a] [REV]...'))
2963 2964 def finish(ui, repo, *revrange, **opts):
2964 2965 """move applied patches into repository history
2965 2966
2966 2967 Finishes the specified revisions (corresponding to applied
2967 2968 patches) by moving them out of mq control into regular repository
2968 2969 history.
2969 2970
2970 2971 Accepts a revision range or the -a/--applied option. If --applied
2971 2972 is specified, all applied mq revisions are removed from mq
2972 2973 control. Otherwise, the given revisions must be at the base of the
2973 2974 stack of applied patches.
2974 2975
2975 2976 This can be especially useful if your changes have been applied to
2976 2977 an upstream repository, or if you are about to push your changes
2977 2978 to upstream.
2978 2979
2979 2980 Returns 0 on success.
2980 2981 """
2981 2982 if not opts.get('applied') and not revrange:
2982 2983 raise util.Abort(_('no revisions specified'))
2983 2984 elif opts.get('applied'):
2984 2985 revrange = ('qbase::qtip',) + revrange
2985 2986
2986 2987 q = repo.mq
2987 2988 if not q.applied:
2988 2989 ui.status(_('no patches applied\n'))
2989 2990 return 0
2990 2991
2991 2992 revs = scmutil.revrange(repo, revrange)
2992 2993 if repo['.'].rev() in revs and repo[None].files():
2993 2994 ui.warn(_('warning: uncommitted changes in the working directory\n'))
2994 2995 # queue.finish may changes phases but leave the responsability to lock the
2995 2996 # repo to the caller to avoid deadlock with wlock. This command code is
2996 2997 # responsability for this locking.
2997 2998 lock = repo.lock()
2998 2999 try:
2999 3000 q.finish(repo, revs)
3000 3001 q.savedirty()
3001 3002 finally:
3002 3003 lock.release()
3003 3004 return 0
3004 3005
3005 3006 @command("qqueue",
3006 3007 [('l', 'list', False, _('list all available queues')),
3007 3008 ('', 'active', False, _('print name of active queue')),
3008 3009 ('c', 'create', False, _('create new queue')),
3009 3010 ('', 'rename', False, _('rename active queue')),
3010 3011 ('', 'delete', False, _('delete reference to queue')),
3011 3012 ('', 'purge', False, _('delete queue, and remove patch dir')),
3012 3013 ],
3013 3014 _('[OPTION] [QUEUE]'))
3014 3015 def qqueue(ui, repo, name=None, **opts):
3015 3016 '''manage multiple patch queues
3016 3017
3017 3018 Supports switching between different patch queues, as well as creating
3018 3019 new patch queues and deleting existing ones.
3019 3020
3020 3021 Omitting a queue name or specifying -l/--list will show you the registered
3021 3022 queues - by default the "normal" patches queue is registered. The currently
3022 3023 active queue will be marked with "(active)". Specifying --active will print
3023 3024 only the name of the active queue.
3024 3025
3025 3026 To create a new queue, use -c/--create. The queue is automatically made
3026 3027 active, except in the case where there are applied patches from the
3027 3028 currently active queue in the repository. Then the queue will only be
3028 3029 created and switching will fail.
3029 3030
3030 3031 To delete an existing queue, use --delete. You cannot delete the currently
3031 3032 active queue.
3032 3033
3033 3034 Returns 0 on success.
3034 3035 '''
3035 3036 q = repo.mq
3036 3037 _defaultqueue = 'patches'
3037 3038 _allqueues = 'patches.queues'
3038 3039 _activequeue = 'patches.queue'
3039 3040
3040 3041 def _getcurrent():
3041 3042 cur = os.path.basename(q.path)
3042 3043 if cur.startswith('patches-'):
3043 3044 cur = cur[8:]
3044 3045 return cur
3045 3046
3046 3047 def _noqueues():
3047 3048 try:
3048 3049 fh = repo.opener(_allqueues, 'r')
3049 3050 fh.close()
3050 3051 except IOError:
3051 3052 return True
3052 3053
3053 3054 return False
3054 3055
3055 3056 def _getqueues():
3056 3057 current = _getcurrent()
3057 3058
3058 3059 try:
3059 3060 fh = repo.opener(_allqueues, 'r')
3060 3061 queues = [queue.strip() for queue in fh if queue.strip()]
3061 3062 fh.close()
3062 3063 if current not in queues:
3063 3064 queues.append(current)
3064 3065 except IOError:
3065 3066 queues = [_defaultqueue]
3066 3067
3067 3068 return sorted(queues)
3068 3069
3069 3070 def _setactive(name):
3070 3071 if q.applied:
3071 3072 raise util.Abort(_('patches applied - cannot set new queue active'))
3072 3073 _setactivenocheck(name)
3073 3074
3074 3075 def _setactivenocheck(name):
3075 3076 fh = repo.opener(_activequeue, 'w')
3076 3077 if name != 'patches':
3077 3078 fh.write(name)
3078 3079 fh.close()
3079 3080
3080 3081 def _addqueue(name):
3081 3082 fh = repo.opener(_allqueues, 'a')
3082 3083 fh.write('%s\n' % (name,))
3083 3084 fh.close()
3084 3085
3085 3086 def _queuedir(name):
3086 3087 if name == 'patches':
3087 3088 return repo.join('patches')
3088 3089 else:
3089 3090 return repo.join('patches-' + name)
3090 3091
3091 3092 def _validname(name):
3092 3093 for n in name:
3093 3094 if n in ':\\/.':
3094 3095 return False
3095 3096 return True
3096 3097
3097 3098 def _delete(name):
3098 3099 if name not in existing:
3099 3100 raise util.Abort(_('cannot delete queue that does not exist'))
3100 3101
3101 3102 current = _getcurrent()
3102 3103
3103 3104 if name == current:
3104 3105 raise util.Abort(_('cannot delete currently active queue'))
3105 3106
3106 3107 fh = repo.opener('patches.queues.new', 'w')
3107 3108 for queue in existing:
3108 3109 if queue == name:
3109 3110 continue
3110 3111 fh.write('%s\n' % (queue,))
3111 3112 fh.close()
3112 3113 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3113 3114
3114 3115 if not name or opts.get('list') or opts.get('active'):
3115 3116 current = _getcurrent()
3116 3117 if opts.get('active'):
3117 3118 ui.write('%s\n' % (current,))
3118 3119 return
3119 3120 for queue in _getqueues():
3120 3121 ui.write('%s' % (queue,))
3121 3122 if queue == current and not ui.quiet:
3122 3123 ui.write(_(' (active)\n'))
3123 3124 else:
3124 3125 ui.write('\n')
3125 3126 return
3126 3127
3127 3128 if not _validname(name):
3128 3129 raise util.Abort(
3129 3130 _('invalid queue name, may not contain the characters ":\\/."'))
3130 3131
3131 3132 existing = _getqueues()
3132 3133
3133 3134 if opts.get('create'):
3134 3135 if name in existing:
3135 3136 raise util.Abort(_('queue "%s" already exists') % name)
3136 3137 if _noqueues():
3137 3138 _addqueue(_defaultqueue)
3138 3139 _addqueue(name)
3139 3140 _setactive(name)
3140 3141 elif opts.get('rename'):
3141 3142 current = _getcurrent()
3142 3143 if name == current:
3143 3144 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3144 3145 if name in existing:
3145 3146 raise util.Abort(_('queue "%s" already exists') % name)
3146 3147
3147 3148 olddir = _queuedir(current)
3148 3149 newdir = _queuedir(name)
3149 3150
3150 3151 if os.path.exists(newdir):
3151 3152 raise util.Abort(_('non-queue directory "%s" already exists') %
3152 3153 newdir)
3153 3154
3154 3155 fh = repo.opener('patches.queues.new', 'w')
3155 3156 for queue in existing:
3156 3157 if queue == current:
3157 3158 fh.write('%s\n' % (name,))
3158 3159 if os.path.exists(olddir):
3159 3160 util.rename(olddir, newdir)
3160 3161 else:
3161 3162 fh.write('%s\n' % (queue,))
3162 3163 fh.close()
3163 3164 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3164 3165 _setactivenocheck(name)
3165 3166 elif opts.get('delete'):
3166 3167 _delete(name)
3167 3168 elif opts.get('purge'):
3168 3169 if name in existing:
3169 3170 _delete(name)
3170 3171 qdir = _queuedir(name)
3171 3172 if os.path.exists(qdir):
3172 3173 shutil.rmtree(qdir)
3173 3174 else:
3174 3175 if name not in existing:
3175 3176 raise util.Abort(_('use --create to create a new queue'))
3176 3177 _setactive(name)
3177 3178
3178 3179 def mqphasedefaults(repo, roots):
3179 3180 """callback used to set mq changeset as secret when no phase data exists"""
3180 3181 if repo.mq.applied:
3181 3182 if repo.ui.configbool('mq', 'secret', False):
3182 3183 mqphase = phases.secret
3183 3184 else:
3184 3185 mqphase = phases.draft
3185 3186 qbase = repo[repo.mq.applied[0].node]
3186 3187 roots[mqphase].add(qbase.node())
3187 3188 return roots
3188 3189
3189 3190 def reposetup(ui, repo):
3190 3191 class mqrepo(repo.__class__):
3191 3192 @util.propertycache
3192 3193 def mq(self):
3193 3194 return queue(self.ui, self.path)
3194 3195
3195 3196 def abortifwdirpatched(self, errmsg, force=False):
3196 3197 if self.mq.applied and not force:
3197 3198 parents = self.dirstate.parents()
3198 3199 patches = [s.node for s in self.mq.applied]
3199 3200 if parents[0] in patches or parents[1] in patches:
3200 3201 raise util.Abort(errmsg)
3201 3202
3202 3203 def commit(self, text="", user=None, date=None, match=None,
3203 3204 force=False, editor=False, extra={}):
3204 3205 self.abortifwdirpatched(
3205 3206 _('cannot commit over an applied mq patch'),
3206 3207 force)
3207 3208
3208 3209 return super(mqrepo, self).commit(text, user, date, match, force,
3209 3210 editor, extra)
3210 3211
3211 3212 def checkpush(self, force, revs):
3212 3213 if self.mq.applied and not force:
3213 3214 outapplied = [e.node for e in self.mq.applied]
3214 3215 if revs:
3215 3216 # Assume applied patches have no non-patch descendants and
3216 3217 # are not on remote already. Filtering any changeset not
3217 3218 # pushed.
3218 3219 heads = set(revs)
3219 3220 for node in reversed(outapplied):
3220 3221 if node in heads:
3221 3222 break
3222 3223 else:
3223 3224 outapplied.pop()
3224 3225 # looking for pushed and shared changeset
3225 3226 for node in outapplied:
3226 3227 if repo[node].phase() < phases.secret:
3227 3228 raise util.Abort(_('source has mq patches applied'))
3228 3229 # no non-secret patches pushed
3229 3230 super(mqrepo, self).checkpush(force, revs)
3230 3231
3231 3232 def _findtags(self):
3232 3233 '''augment tags from base class with patch tags'''
3233 3234 result = super(mqrepo, self)._findtags()
3234 3235
3235 3236 q = self.mq
3236 3237 if not q.applied:
3237 3238 return result
3238 3239
3239 3240 mqtags = [(patch.node, patch.name) for patch in q.applied]
3240 3241
3241 3242 try:
3242 3243 self.changelog.rev(mqtags[-1][0])
3243 3244 except error.LookupError:
3244 3245 self.ui.warn(_('mq status file refers to unknown node %s\n')
3245 3246 % short(mqtags[-1][0]))
3246 3247 return result
3247 3248
3248 3249 mqtags.append((mqtags[-1][0], 'qtip'))
3249 3250 mqtags.append((mqtags[0][0], 'qbase'))
3250 3251 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3251 3252 tags = result[0]
3252 3253 for patch in mqtags:
3253 3254 if patch[1] in tags:
3254 3255 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3255 3256 % patch[1])
3256 3257 else:
3257 3258 tags[patch[1]] = patch[0]
3258 3259
3259 3260 return result
3260 3261
3261 3262 def _branchtags(self, partial, lrev):
3262 3263 q = self.mq
3263 3264 cl = self.changelog
3264 3265 qbase = None
3265 3266 if not q.applied:
3266 3267 if getattr(self, '_committingpatch', False):
3267 3268 # Committing a new patch, must be tip
3268 3269 qbase = len(cl) - 1
3269 3270 else:
3270 3271 qbasenode = q.applied[0].node
3271 3272 try:
3272 3273 qbase = cl.rev(qbasenode)
3273 3274 except error.LookupError:
3274 3275 self.ui.warn(_('mq status file refers to unknown node %s\n')
3275 3276 % short(qbasenode))
3276 3277 if qbase is None:
3277 3278 return super(mqrepo, self)._branchtags(partial, lrev)
3278 3279
3279 3280 start = lrev + 1
3280 3281 if start < qbase:
3281 3282 # update the cache (excluding the patches) and save it
3282 3283 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3283 3284 self._updatebranchcache(partial, ctxgen)
3284 3285 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3285 3286 start = qbase
3286 3287 # if start = qbase, the cache is as updated as it should be.
3287 3288 # if start > qbase, the cache includes (part of) the patches.
3288 3289 # we might as well use it, but we won't save it.
3289 3290
3290 3291 # update the cache up to the tip
3291 3292 ctxgen = (self[r] for r in xrange(start, len(cl)))
3292 3293 self._updatebranchcache(partial, ctxgen)
3293 3294
3294 3295 return partial
3295 3296
3296 3297 if repo.local():
3297 3298 repo.__class__ = mqrepo
3298 3299
3299 3300 repo._phasedefaults.append(mqphasedefaults)
3300 3301
3301 3302 def mqimport(orig, ui, repo, *args, **kwargs):
3302 3303 if (hasattr(repo, 'abortifwdirpatched')
3303 3304 and not kwargs.get('no_commit', False)):
3304 3305 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3305 3306 kwargs.get('force'))
3306 3307 return orig(ui, repo, *args, **kwargs)
3307 3308
3308 3309 def mqinit(orig, ui, *args, **kwargs):
3309 3310 mq = kwargs.pop('mq', None)
3310 3311
3311 3312 if not mq:
3312 3313 return orig(ui, *args, **kwargs)
3313 3314
3314 3315 if args:
3315 3316 repopath = args[0]
3316 3317 if not hg.islocal(repopath):
3317 3318 raise util.Abort(_('only a local queue repository '
3318 3319 'may be initialized'))
3319 3320 else:
3320 3321 repopath = cmdutil.findrepo(os.getcwd())
3321 3322 if not repopath:
3322 3323 raise util.Abort(_('there is no Mercurial repository here '
3323 3324 '(.hg not found)'))
3324 3325 repo = hg.repository(ui, repopath)
3325 3326 return qinit(ui, repo, True)
3326 3327
3327 3328 def mqcommand(orig, ui, repo, *args, **kwargs):
3328 3329 """Add --mq option to operate on patch repository instead of main"""
3329 3330
3330 3331 # some commands do not like getting unknown options
3331 3332 mq = kwargs.pop('mq', None)
3332 3333
3333 3334 if not mq:
3334 3335 return orig(ui, repo, *args, **kwargs)
3335 3336
3336 3337 q = repo.mq
3337 3338 r = q.qrepo()
3338 3339 if not r:
3339 3340 raise util.Abort(_('no queue repository'))
3340 3341 return orig(r.ui, r, *args, **kwargs)
3341 3342
3342 3343 def summary(orig, ui, repo, *args, **kwargs):
3343 3344 r = orig(ui, repo, *args, **kwargs)
3344 3345 q = repo.mq
3345 3346 m = []
3346 3347 a, u = len(q.applied), len(q.unapplied(repo))
3347 3348 if a:
3348 3349 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3349 3350 if u:
3350 3351 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3351 3352 if m:
3352 3353 ui.write("mq: %s\n" % ', '.join(m))
3353 3354 else:
3354 3355 ui.note(_("mq: (empty queue)\n"))
3355 3356 return r
3356 3357
3357 3358 def revsetmq(repo, subset, x):
3358 3359 """``mq()``
3359 3360 Changesets managed by MQ.
3360 3361 """
3361 3362 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3362 3363 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3363 3364 return [r for r in subset if r in applied]
3364 3365
3365 3366 def extsetup(ui):
3366 3367 revset.symbols['mq'] = revsetmq
3367 3368
3368 3369 # tell hggettext to extract docstrings from these functions:
3369 3370 i18nfunctions = [revsetmq]
3370 3371
3371 3372 def uisetup(ui):
3372 3373 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3373 3374
3374 3375 extensions.wrapcommand(commands.table, 'import', mqimport)
3375 3376 extensions.wrapcommand(commands.table, 'summary', summary)
3376 3377
3377 3378 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3378 3379 entry[1].extend(mqopt)
3379 3380
3380 3381 nowrap = set(commands.norepo.split(" "))
3381 3382
3382 3383 def dotable(cmdtable):
3383 3384 for cmd in cmdtable.keys():
3384 3385 cmd = cmdutil.parsealiases(cmd)[0]
3385 3386 if cmd in nowrap:
3386 3387 continue
3387 3388 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3388 3389 entry[1].extend(mqopt)
3389 3390
3390 3391 dotable(commands.table)
3391 3392
3392 3393 for extname, extmodule in extensions.extensions():
3393 3394 if extmodule.__file__ != __file__:
3394 3395 dotable(getattr(extmodule, 'cmdtable', {}))
3395 3396
3396 3397
3397 3398 colortable = {'qguard.negative': 'red',
3398 3399 'qguard.positive': 'yellow',
3399 3400 'qguard.unguarded': 'green',
3400 3401 'qseries.applied': 'blue bold underline',
3401 3402 'qseries.guarded': 'black bold',
3402 3403 'qseries.missing': 'red bold',
3403 3404 'qseries.unapplied': 'black bold'}
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now