PageRenderTime 96ms CodeModel.GetById 40ms RepoModel.GetById 0ms app.codeStats 1ms

/hgext/convert/p4.py

https://bitbucket.org/mirror/mercurial/
Python | 203 lines | 184 code | 12 blank | 7 comment | 11 complexity | 4bc7031fc2f3c09523754971ebd671ec MD5 | raw file
Possible License(s): GPL-2.0
  1. # Perforce source for convert extension.
  2. #
  3. # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
  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. from mercurial import util
  8. from mercurial.i18n import _
  9. from common import commit, converter_source, checktool, NoRepo
  10. import marshal
  11. import re
  12. def loaditer(f):
  13. "Yield the dictionary objects generated by p4"
  14. try:
  15. while True:
  16. d = marshal.load(f)
  17. if not d:
  18. break
  19. yield d
  20. except EOFError:
  21. pass
  22. class p4_source(converter_source):
  23. def __init__(self, ui, path, rev=None):
  24. super(p4_source, self).__init__(ui, path, rev=rev)
  25. if "/" in path and not path.startswith('//'):
  26. raise NoRepo(_('%s does not look like a P4 repository') % path)
  27. checktool('p4', abort=False)
  28. self.p4changes = {}
  29. self.heads = {}
  30. self.changeset = {}
  31. self.files = {}
  32. self.tags = {}
  33. self.lastbranch = {}
  34. self.parent = {}
  35. self.encoding = "latin_1"
  36. self.depotname = {} # mapping from local name to depot name
  37. self.re_type = re.compile(
  38. "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
  39. "(\+\w+)?$")
  40. self.re_keywords = re.compile(
  41. r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
  42. r":[^$\n]*\$")
  43. self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
  44. self._parse(ui, path)
  45. def _parse_view(self, path):
  46. "Read changes affecting the path"
  47. cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
  48. stdout = util.popen(cmd, mode='rb')
  49. for d in loaditer(stdout):
  50. c = d.get("change", None)
  51. if c:
  52. self.p4changes[c] = True
  53. def _parse(self, ui, path):
  54. "Prepare list of P4 filenames and revisions to import"
  55. ui.status(_('reading p4 views\n'))
  56. # read client spec or view
  57. if "/" in path:
  58. self._parse_view(path)
  59. if path.startswith("//") and path.endswith("/..."):
  60. views = {path[:-3]:""}
  61. else:
  62. views = {"//": ""}
  63. else:
  64. cmd = 'p4 -G client -o %s' % util.shellquote(path)
  65. clientspec = marshal.load(util.popen(cmd, mode='rb'))
  66. views = {}
  67. for client in clientspec:
  68. if client.startswith("View"):
  69. sview, cview = clientspec[client].split()
  70. self._parse_view(sview)
  71. if sview.endswith("...") and cview.endswith("..."):
  72. sview = sview[:-3]
  73. cview = cview[:-3]
  74. cview = cview[2:]
  75. cview = cview[cview.find("/") + 1:]
  76. views[sview] = cview
  77. # list of changes that affect our source files
  78. self.p4changes = self.p4changes.keys()
  79. self.p4changes.sort(key=int)
  80. # list with depot pathnames, longest first
  81. vieworder = views.keys()
  82. vieworder.sort(key=len, reverse=True)
  83. # handle revision limiting
  84. startrev = self.ui.config('convert', 'p4.startrev', default=0)
  85. self.p4changes = [x for x in self.p4changes
  86. if ((not startrev or int(x) >= int(startrev)) and
  87. (not self.rev or int(x) <= int(self.rev)))]
  88. # now read the full changelists to get the list of file revisions
  89. ui.status(_('collecting p4 changelists\n'))
  90. lastid = None
  91. for change in self.p4changes:
  92. cmd = "p4 -G describe -s %s" % change
  93. stdout = util.popen(cmd, mode='rb')
  94. d = marshal.load(stdout)
  95. desc = self.recode(d.get("desc", ""))
  96. shortdesc = desc.split("\n", 1)[0]
  97. t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
  98. ui.status(util.ellipsis(t, 80) + '\n')
  99. if lastid:
  100. parents = [lastid]
  101. else:
  102. parents = []
  103. date = (int(d["time"]), 0) # timezone not set
  104. c = commit(author=self.recode(d["user"]),
  105. date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
  106. parents=parents, desc=desc, branch='',
  107. extra={"p4": change})
  108. files = []
  109. i = 0
  110. while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
  111. oldname = d["depotFile%d" % i]
  112. filename = None
  113. for v in vieworder:
  114. if oldname.startswith(v):
  115. filename = views[v] + oldname[len(v):]
  116. break
  117. if filename:
  118. files.append((filename, d["rev%d" % i]))
  119. self.depotname[filename] = oldname
  120. i += 1
  121. self.changeset[change] = c
  122. self.files[change] = files
  123. lastid = change
  124. if lastid:
  125. self.heads = [lastid]
  126. def getheads(self):
  127. return self.heads
  128. def getfile(self, name, rev):
  129. cmd = 'p4 -G print %s' \
  130. % util.shellquote("%s#%s" % (self.depotname[name], rev))
  131. stdout = util.popen(cmd, mode='rb')
  132. mode = None
  133. contents = ""
  134. keywords = None
  135. for d in loaditer(stdout):
  136. code = d["code"]
  137. data = d.get("data")
  138. if code == "error":
  139. raise IOError(d["generic"], data)
  140. elif code == "stat":
  141. p4type = self.re_type.match(d["type"])
  142. if p4type:
  143. mode = ""
  144. flags = (p4type.group(1) or "") + (p4type.group(3) or "")
  145. if "x" in flags:
  146. mode = "x"
  147. if p4type.group(2) == "symlink":
  148. mode = "l"
  149. if "ko" in flags:
  150. keywords = self.re_keywords_old
  151. elif "k" in flags:
  152. keywords = self.re_keywords
  153. elif code == "text" or code == "binary":
  154. contents += data
  155. if mode is None:
  156. raise IOError(0, "bad stat")
  157. if keywords:
  158. contents = keywords.sub("$\\1$", contents)
  159. if mode == "l" and contents.endswith("\n"):
  160. contents = contents[:-1]
  161. return contents, mode
  162. def getchanges(self, rev):
  163. return self.files[rev], {}
  164. def getcommit(self, rev):
  165. return self.changeset[rev]
  166. def gettags(self):
  167. return self.tags
  168. def getchangedfiles(self, rev, i):
  169. return sorted([x[0] for x in self.files[rev]])