PageRenderTime 64ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/hgext/mq.py

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