PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/hg4idea/testData/bin/hgext/largefiles/lfutil.py

http://github.com/JetBrains/intellij-community
Python | 367 lines | 291 code | 32 blank | 44 comment | 52 complexity | 7378080432ce415fec2b971107ce5e24 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, MPL-2.0-no-copyleft-exception, MIT, EPL-1.0, AGPL-1.0
  1. # Copyright 2009-2010 Gregory P. Ward
  2. # Copyright 2009-2010 Intelerad Medical Systems Incorporated
  3. # Copyright 2010-2011 Fog Creek Software
  4. # Copyright 2010-2011 Unity Technologies
  5. #
  6. # This software may be used and distributed according to the terms of the
  7. # GNU General Public License version 2 or any later version.
  8. '''largefiles utility code: must not import other modules in this package.'''
  9. import os
  10. import platform
  11. import shutil
  12. import stat
  13. from mercurial import dirstate, httpconnection, match as match_, util, scmutil
  14. from mercurial.i18n import _
  15. shortname = '.hglf'
  16. shortnameslash = shortname + '/'
  17. longname = 'largefiles'
  18. # -- Private worker functions ------------------------------------------
  19. def getminsize(ui, assumelfiles, opt, default=10):
  20. lfsize = opt
  21. if not lfsize and assumelfiles:
  22. lfsize = ui.config(longname, 'minsize', default=default)
  23. if lfsize:
  24. try:
  25. lfsize = float(lfsize)
  26. except ValueError:
  27. raise util.Abort(_('largefiles: size must be number (not %s)\n')
  28. % lfsize)
  29. if lfsize is None:
  30. raise util.Abort(_('minimum size for largefiles must be specified'))
  31. return lfsize
  32. def link(src, dest):
  33. util.makedirs(os.path.dirname(dest))
  34. try:
  35. util.oslink(src, dest)
  36. except OSError:
  37. # if hardlinks fail, fallback on atomic copy
  38. dst = util.atomictempfile(dest)
  39. for chunk in util.filechunkiter(open(src, 'rb')):
  40. dst.write(chunk)
  41. dst.close()
  42. os.chmod(dest, os.stat(src).st_mode)
  43. def usercachepath(ui, hash):
  44. path = ui.configpath(longname, 'usercache', None)
  45. if path:
  46. path = os.path.join(path, hash)
  47. else:
  48. if os.name == 'nt':
  49. appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
  50. if appdata:
  51. path = os.path.join(appdata, longname, hash)
  52. elif platform.system() == 'Darwin':
  53. home = os.getenv('HOME')
  54. if home:
  55. path = os.path.join(home, 'Library', 'Caches',
  56. longname, hash)
  57. elif os.name == 'posix':
  58. path = os.getenv('XDG_CACHE_HOME')
  59. if path:
  60. path = os.path.join(path, longname, hash)
  61. else:
  62. home = os.getenv('HOME')
  63. if home:
  64. path = os.path.join(home, '.cache', longname, hash)
  65. else:
  66. raise util.Abort(_('unknown operating system: %s\n') % os.name)
  67. return path
  68. def inusercache(ui, hash):
  69. path = usercachepath(ui, hash)
  70. return path and os.path.exists(path)
  71. def findfile(repo, hash):
  72. if instore(repo, hash):
  73. repo.ui.note(_('found %s in store\n') % hash)
  74. return storepath(repo, hash)
  75. elif inusercache(repo.ui, hash):
  76. repo.ui.note(_('found %s in system cache\n') % hash)
  77. path = storepath(repo, hash)
  78. link(usercachepath(repo.ui, hash), path)
  79. return path
  80. return None
  81. class largefilesdirstate(dirstate.dirstate):
  82. def __getitem__(self, key):
  83. return super(largefilesdirstate, self).__getitem__(unixpath(key))
  84. def normal(self, f):
  85. return super(largefilesdirstate, self).normal(unixpath(f))
  86. def remove(self, f):
  87. return super(largefilesdirstate, self).remove(unixpath(f))
  88. def add(self, f):
  89. return super(largefilesdirstate, self).add(unixpath(f))
  90. def drop(self, f):
  91. return super(largefilesdirstate, self).drop(unixpath(f))
  92. def forget(self, f):
  93. return super(largefilesdirstate, self).forget(unixpath(f))
  94. def normallookup(self, f):
  95. return super(largefilesdirstate, self).normallookup(unixpath(f))
  96. def _ignore(self):
  97. return False
  98. def openlfdirstate(ui, repo, create=True):
  99. '''
  100. Return a dirstate object that tracks largefiles: i.e. its root is
  101. the repo root, but it is saved in .hg/largefiles/dirstate.
  102. '''
  103. lfstoredir = repo.join(longname)
  104. opener = scmutil.opener(lfstoredir)
  105. lfdirstate = largefilesdirstate(opener, ui, repo.root,
  106. repo.dirstate._validate)
  107. # If the largefiles dirstate does not exist, populate and create
  108. # it. This ensures that we create it on the first meaningful
  109. # largefiles operation in a new clone.
  110. if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
  111. util.makedirs(lfstoredir)
  112. matcher = getstandinmatcher(repo)
  113. for standin in repo.dirstate.walk(matcher, [], False, False):
  114. lfile = splitstandin(standin)
  115. lfdirstate.normallookup(lfile)
  116. return lfdirstate
  117. def lfdirstatestatus(lfdirstate, repo, rev):
  118. match = match_.always(repo.root, repo.getcwd())
  119. s = lfdirstate.status(match, [], False, False, False)
  120. unsure, modified, added, removed, missing, unknown, ignored, clean = s
  121. for lfile in unsure:
  122. try:
  123. fctx = repo[rev][standin(lfile)]
  124. except LookupError:
  125. fctx = None
  126. if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
  127. modified.append(lfile)
  128. else:
  129. clean.append(lfile)
  130. lfdirstate.normal(lfile)
  131. return (modified, added, removed, missing, unknown, ignored, clean)
  132. def listlfiles(repo, rev=None, matcher=None):
  133. '''return a list of largefiles in the working copy or the
  134. specified changeset'''
  135. if matcher is None:
  136. matcher = getstandinmatcher(repo)
  137. # ignore unknown files in working directory
  138. return [splitstandin(f)
  139. for f in repo[rev].walk(matcher)
  140. if rev is not None or repo.dirstate[f] != '?']
  141. def instore(repo, hash):
  142. return os.path.exists(storepath(repo, hash))
  143. def storepath(repo, hash):
  144. return repo.join(os.path.join(longname, hash))
  145. def copyfromcache(repo, hash, filename):
  146. '''Copy the specified largefile from the repo or system cache to
  147. filename in the repository. Return true on success or false if the
  148. file was not found in either cache (which should not happened:
  149. this is meant to be called only after ensuring that the needed
  150. largefile exists in the cache).'''
  151. path = findfile(repo, hash)
  152. if path is None:
  153. return False
  154. util.makedirs(os.path.dirname(repo.wjoin(filename)))
  155. # The write may fail before the file is fully written, but we
  156. # don't use atomic writes in the working copy.
  157. shutil.copy(path, repo.wjoin(filename))
  158. return True
  159. def copytostore(repo, rev, file, uploaded=False):
  160. hash = readstandin(repo, file, rev)
  161. if instore(repo, hash):
  162. return
  163. copytostoreabsolute(repo, repo.wjoin(file), hash)
  164. def copyalltostore(repo, node):
  165. '''Copy all largefiles in a given revision to the store'''
  166. ctx = repo[node]
  167. for filename in ctx.files():
  168. if isstandin(filename) and filename in ctx.manifest():
  169. realfile = splitstandin(filename)
  170. copytostore(repo, ctx.node(), realfile)
  171. def copytostoreabsolute(repo, file, hash):
  172. if inusercache(repo.ui, hash):
  173. link(usercachepath(repo.ui, hash), storepath(repo, hash))
  174. elif not getattr(repo, "_isconverting", False):
  175. util.makedirs(os.path.dirname(storepath(repo, hash)))
  176. dst = util.atomictempfile(storepath(repo, hash),
  177. createmode=repo.store.createmode)
  178. for chunk in util.filechunkiter(open(file, 'rb')):
  179. dst.write(chunk)
  180. dst.close()
  181. linktousercache(repo, hash)
  182. def linktousercache(repo, hash):
  183. path = usercachepath(repo.ui, hash)
  184. if path:
  185. link(storepath(repo, hash), path)
  186. def getstandinmatcher(repo, pats=[], opts={}):
  187. '''Return a match object that applies pats to the standin directory'''
  188. standindir = repo.wjoin(shortname)
  189. if pats:
  190. pats = [os.path.join(standindir, pat) for pat in pats]
  191. else:
  192. # no patterns: relative to repo root
  193. pats = [standindir]
  194. # no warnings about missing files or directories
  195. match = scmutil.match(repo[None], pats, opts)
  196. match.bad = lambda f, msg: None
  197. return match
  198. def composestandinmatcher(repo, rmatcher):
  199. '''Return a matcher that accepts standins corresponding to the
  200. files accepted by rmatcher. Pass the list of files in the matcher
  201. as the paths specified by the user.'''
  202. smatcher = getstandinmatcher(repo, rmatcher.files())
  203. isstandin = smatcher.matchfn
  204. def composedmatchfn(f):
  205. return isstandin(f) and rmatcher.matchfn(splitstandin(f))
  206. smatcher.matchfn = composedmatchfn
  207. return smatcher
  208. def standin(filename):
  209. '''Return the repo-relative path to the standin for the specified big
  210. file.'''
  211. # Notes:
  212. # 1) Some callers want an absolute path, but for instance addlargefiles
  213. # needs it repo-relative so it can be passed to repo[None].add(). So
  214. # leave it up to the caller to use repo.wjoin() to get an absolute path.
  215. # 2) Join with '/' because that's what dirstate always uses, even on
  216. # Windows. Change existing separator to '/' first in case we are
  217. # passed filenames from an external source (like the command line).
  218. return shortnameslash + util.pconvert(filename)
  219. def isstandin(filename):
  220. '''Return true if filename is a big file standin. filename must be
  221. in Mercurial's internal form (slash-separated).'''
  222. return filename.startswith(shortnameslash)
  223. def splitstandin(filename):
  224. # Split on / because that's what dirstate always uses, even on Windows.
  225. # Change local separator to / first just in case we are passed filenames
  226. # from an external source (like the command line).
  227. bits = util.pconvert(filename).split('/', 1)
  228. if len(bits) == 2 and bits[0] == shortname:
  229. return bits[1]
  230. else:
  231. return None
  232. def updatestandin(repo, standin):
  233. file = repo.wjoin(splitstandin(standin))
  234. if os.path.exists(file):
  235. hash = hashfile(file)
  236. executable = getexecutable(file)
  237. writestandin(repo, standin, hash, executable)
  238. def readstandin(repo, filename, node=None):
  239. '''read hex hash from standin for filename at given node, or working
  240. directory if no node is given'''
  241. return repo[node][standin(filename)].data().strip()
  242. def writestandin(repo, standin, hash, executable):
  243. '''write hash to <repo.root>/<standin>'''
  244. repo.wwrite(standin, hash + '\n', executable and 'x' or '')
  245. def copyandhash(instream, outfile):
  246. '''Read bytes from instream (iterable) and write them to outfile,
  247. computing the SHA-1 hash of the data along the way. Return the hash.'''
  248. hasher = util.sha1('')
  249. for data in instream:
  250. hasher.update(data)
  251. outfile.write(data)
  252. return hasher.hexdigest()
  253. def hashrepofile(repo, file):
  254. return hashfile(repo.wjoin(file))
  255. def hashfile(file):
  256. if not os.path.exists(file):
  257. return ''
  258. hasher = util.sha1('')
  259. fd = open(file, 'rb')
  260. for data in util.filechunkiter(fd, 128 * 1024):
  261. hasher.update(data)
  262. fd.close()
  263. return hasher.hexdigest()
  264. def getexecutable(filename):
  265. mode = os.stat(filename).st_mode
  266. return ((mode & stat.S_IXUSR) and
  267. (mode & stat.S_IXGRP) and
  268. (mode & stat.S_IXOTH))
  269. def urljoin(first, second, *arg):
  270. def join(left, right):
  271. if not left.endswith('/'):
  272. left += '/'
  273. if right.startswith('/'):
  274. right = right[1:]
  275. return left + right
  276. url = join(first, second)
  277. for a in arg:
  278. url = join(url, a)
  279. return url
  280. def hexsha1(data):
  281. """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
  282. object data"""
  283. h = util.sha1()
  284. for chunk in util.filechunkiter(data):
  285. h.update(chunk)
  286. return h.hexdigest()
  287. def httpsendfile(ui, filename):
  288. return httpconnection.httpsendfile(ui, filename, 'rb')
  289. def unixpath(path):
  290. '''Return a version of path normalized for use with the lfdirstate.'''
  291. return util.pconvert(os.path.normpath(path))
  292. def islfilesrepo(repo):
  293. if ('largefiles' in repo.requirements and
  294. util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
  295. return True
  296. return util.any(openlfdirstate(repo.ui, repo, False))
  297. class storeprotonotcapable(Exception):
  298. def __init__(self, storetypes):
  299. self.storetypes = storetypes
  300. def getstandinsstate(repo):
  301. standins = []
  302. matcher = getstandinmatcher(repo)
  303. for standin in repo.dirstate.walk(matcher, [], False, False):
  304. lfile = splitstandin(standin)
  305. try:
  306. hash = readstandin(repo, lfile)
  307. except IOError:
  308. hash = None
  309. standins.append((lfile, hash))
  310. return standins
  311. def getlfilestoupdate(oldstandins, newstandins):
  312. changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
  313. filelist = []
  314. for f in changedstandins:
  315. if f[0] not in filelist:
  316. filelist.append(f[0])
  317. return filelist