summaryrefslogtreecommitdiff
path: root/hgext/largefiles/lfutil.py
diff options
context:
space:
mode:
Diffstat (limited to 'hgext/largefiles/lfutil.py')
-rw-r--r--hgext/largefiles/lfutil.py467
1 files changed, 0 insertions, 467 deletions
diff --git a/hgext/largefiles/lfutil.py b/hgext/largefiles/lfutil.py
deleted file mode 100644
index 6a64d89..0000000
--- a/hgext/largefiles/lfutil.py
+++ /dev/null
@@ -1,467 +0,0 @@
-# Copyright 2009-2010 Gregory P. Ward
-# Copyright 2009-2010 Intelerad Medical Systems Incorporated
-# Copyright 2010-2011 Fog Creek Software
-# Copyright 2010-2011 Unity Technologies
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-'''largefiles utility code: must not import other modules in this package.'''
-
-import os
-import errno
-import platform
-import shutil
-import stat
-
-from mercurial import dirstate, httpconnection, match as match_, util, scmutil
-from mercurial.i18n import _
-
-shortname = '.hglf'
-longname = 'largefiles'
-
-
-# -- Portability wrappers ----------------------------------------------
-
-def dirstatewalk(dirstate, matcher, unknown=False, ignored=False):
- return dirstate.walk(matcher, [], unknown, ignored)
-
-def repoadd(repo, list):
- add = repo[None].add
- return add(list)
-
-def reporemove(repo, list, unlink=False):
- def remove(list, unlink):
- wlock = repo.wlock()
- try:
- if unlink:
- for f in list:
- try:
- util.unlinkpath(repo.wjoin(f))
- except OSError, inst:
- if inst.errno != errno.ENOENT:
- raise
- repo[None].forget(list)
- finally:
- wlock.release()
- return remove(list, unlink=unlink)
-
-def repoforget(repo, list):
- forget = repo[None].forget
- return forget(list)
-
-def findoutgoing(repo, remote, force):
- from mercurial import discovery
- common, _anyinc, _heads = discovery.findcommonincoming(repo,
- remote.peer(), force=force)
- return repo.changelog.findmissing(common)
-
-# -- Private worker functions ------------------------------------------
-
-def getminsize(ui, assumelfiles, opt, default=10):
- lfsize = opt
- if not lfsize and assumelfiles:
- lfsize = ui.config(longname, 'minsize', default=default)
- if lfsize:
- try:
- lfsize = float(lfsize)
- except ValueError:
- raise util.Abort(_('largefiles: size must be number (not %s)\n')
- % lfsize)
- if lfsize is None:
- raise util.Abort(_('minimum size for largefiles must be specified'))
- return lfsize
-
-def link(src, dest):
- try:
- util.oslink(src, dest)
- except OSError:
- # if hardlinks fail, fallback on atomic copy
- dst = util.atomictempfile(dest)
- for chunk in util.filechunkiter(open(src, 'rb')):
- dst.write(chunk)
- dst.close()
- os.chmod(dest, os.stat(src).st_mode)
-
-def usercachepath(ui, hash):
- path = ui.configpath(longname, 'usercache', None)
- if path:
- path = os.path.join(path, hash)
- else:
- if os.name == 'nt':
- appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
- if appdata:
- path = os.path.join(appdata, longname, hash)
- elif platform.system() == 'Darwin':
- home = os.getenv('HOME')
- if home:
- path = os.path.join(home, 'Library', 'Caches',
- longname, hash)
- elif os.name == 'posix':
- path = os.getenv('XDG_CACHE_HOME')
- if path:
- path = os.path.join(path, longname, hash)
- else:
- home = os.getenv('HOME')
- if home:
- path = os.path.join(home, '.cache', longname, hash)
- else:
- raise util.Abort(_('unknown operating system: %s\n') % os.name)
- return path
-
-def inusercache(ui, hash):
- path = usercachepath(ui, hash)
- return path and os.path.exists(path)
-
-def findfile(repo, hash):
- if instore(repo, hash):
- repo.ui.note(_('found %s in store\n') % hash)
- return storepath(repo, hash)
- elif inusercache(repo.ui, hash):
- repo.ui.note(_('found %s in system cache\n') % hash)
- path = storepath(repo, hash)
- util.makedirs(os.path.dirname(path))
- link(usercachepath(repo.ui, hash), path)
- return path
- return None
-
-class largefilesdirstate(dirstate.dirstate):
- def __getitem__(self, key):
- return super(largefilesdirstate, self).__getitem__(unixpath(key))
- def normal(self, f):
- return super(largefilesdirstate, self).normal(unixpath(f))
- def remove(self, f):
- return super(largefilesdirstate, self).remove(unixpath(f))
- def add(self, f):
- return super(largefilesdirstate, self).add(unixpath(f))
- def drop(self, f):
- return super(largefilesdirstate, self).drop(unixpath(f))
- def forget(self, f):
- return super(largefilesdirstate, self).forget(unixpath(f))
- def normallookup(self, f):
- return super(largefilesdirstate, self).normallookup(unixpath(f))
-
-def openlfdirstate(ui, repo):
- '''
- Return a dirstate object that tracks largefiles: i.e. its root is
- the repo root, but it is saved in .hg/largefiles/dirstate.
- '''
- admin = repo.join(longname)
- opener = scmutil.opener(admin)
- lfdirstate = largefilesdirstate(opener, ui, repo.root,
- repo.dirstate._validate)
-
- # If the largefiles dirstate does not exist, populate and create
- # it. This ensures that we create it on the first meaningful
- # largefiles operation in a new clone.
- if not os.path.exists(os.path.join(admin, 'dirstate')):
- util.makedirs(admin)
- matcher = getstandinmatcher(repo)
- for standin in dirstatewalk(repo.dirstate, matcher):
- lfile = splitstandin(standin)
- hash = readstandin(repo, lfile)
- lfdirstate.normallookup(lfile)
- try:
- if hash == hashfile(repo.wjoin(lfile)):
- lfdirstate.normal(lfile)
- except OSError, err:
- if err.errno != errno.ENOENT:
- raise
- return lfdirstate
-
-def lfdirstatestatus(lfdirstate, repo, rev):
- match = match_.always(repo.root, repo.getcwd())
- s = lfdirstate.status(match, [], False, False, False)
- unsure, modified, added, removed, missing, unknown, ignored, clean = s
- for lfile in unsure:
- if repo[rev][standin(lfile)].data().strip() != \
- hashfile(repo.wjoin(lfile)):
- modified.append(lfile)
- else:
- clean.append(lfile)
- lfdirstate.normal(lfile)
- return (modified, added, removed, missing, unknown, ignored, clean)
-
-def listlfiles(repo, rev=None, matcher=None):
- '''return a list of largefiles in the working copy or the
- specified changeset'''
-
- if matcher is None:
- matcher = getstandinmatcher(repo)
-
- # ignore unknown files in working directory
- return [splitstandin(f)
- for f in repo[rev].walk(matcher)
- if rev is not None or repo.dirstate[f] != '?']
-
-def instore(repo, hash):
- return os.path.exists(storepath(repo, hash))
-
-def storepath(repo, hash):
- return repo.join(os.path.join(longname, hash))
-
-def copyfromcache(repo, hash, filename):
- '''Copy the specified largefile from the repo or system cache to
- filename in the repository. Return true on success or false if the
- file was not found in either cache (which should not happened:
- this is meant to be called only after ensuring that the needed
- largefile exists in the cache).'''
- path = findfile(repo, hash)
- if path is None:
- return False
- util.makedirs(os.path.dirname(repo.wjoin(filename)))
- # The write may fail before the file is fully written, but we
- # don't use atomic writes in the working copy.
- shutil.copy(path, repo.wjoin(filename))
- return True
-
-def copytostore(repo, rev, file, uploaded=False):
- hash = readstandin(repo, file)
- if instore(repo, hash):
- return
- copytostoreabsolute(repo, repo.wjoin(file), hash)
-
-def copyalltostore(repo, node):
- '''Copy all largefiles in a given revision to the store'''
-
- ctx = repo[node]
- for filename in ctx.files():
- if isstandin(filename) and filename in ctx.manifest():
- realfile = splitstandin(filename)
- copytostore(repo, ctx.node(), realfile)
-
-
-def copytostoreabsolute(repo, file, hash):
- util.makedirs(os.path.dirname(storepath(repo, hash)))
- if inusercache(repo.ui, hash):
- link(usercachepath(repo.ui, hash), storepath(repo, hash))
- else:
- dst = util.atomictempfile(storepath(repo, hash),
- createmode=repo.store.createmode)
- for chunk in util.filechunkiter(open(file, 'rb')):
- dst.write(chunk)
- dst.close()
- linktousercache(repo, hash)
-
-def linktousercache(repo, hash):
- path = usercachepath(repo.ui, hash)
- if path:
- util.makedirs(os.path.dirname(path))
- link(storepath(repo, hash), path)
-
-def getstandinmatcher(repo, pats=[], opts={}):
- '''Return a match object that applies pats to the standin directory'''
- standindir = repo.pathto(shortname)
- if pats:
- # patterns supplied: search standin directory relative to current dir
- cwd = repo.getcwd()
- if os.path.isabs(cwd):
- # cwd is an absolute path for hg -R <reponame>
- # work relative to the repository root in this case
- cwd = ''
- pats = [os.path.join(standindir, cwd, pat) for pat in pats]
- elif os.path.isdir(standindir):
- # no patterns: relative to repo root
- pats = [standindir]
- else:
- # no patterns and no standin dir: return matcher that matches nothing
- match = match_.match(repo.root, None, [], exact=True)
- match.matchfn = lambda f: False
- return match
- return getmatcher(repo, pats, opts, showbad=False)
-
-def getmatcher(repo, pats=[], opts={}, showbad=True):
- '''Wrapper around scmutil.match() that adds showbad: if false,
- neuter the match object's bad() method so it does not print any
- warnings about missing files or directories.'''
- match = scmutil.match(repo[None], pats, opts)
-
- if not showbad:
- match.bad = lambda f, msg: None
- return match
-
-def composestandinmatcher(repo, rmatcher):
- '''Return a matcher that accepts standins corresponding to the
- files accepted by rmatcher. Pass the list of files in the matcher
- as the paths specified by the user.'''
- smatcher = getstandinmatcher(repo, rmatcher.files())
- isstandin = smatcher.matchfn
- def composedmatchfn(f):
- return isstandin(f) and rmatcher.matchfn(splitstandin(f))
- smatcher.matchfn = composedmatchfn
-
- return smatcher
-
-def standin(filename):
- '''Return the repo-relative path to the standin for the specified big
- file.'''
- # Notes:
- # 1) Most callers want an absolute path, but _createstandin() needs
- # it repo-relative so lfadd() can pass it to repoadd(). So leave
- # it up to the caller to use repo.wjoin() to get an absolute path.
- # 2) Join with '/' because that's what dirstate always uses, even on
- # Windows. Change existing separator to '/' first in case we are
- # passed filenames from an external source (like the command line).
- return shortname + '/' + util.pconvert(filename)
-
-def isstandin(filename):
- '''Return true if filename is a big file standin. filename must be
- in Mercurial's internal form (slash-separated).'''
- return filename.startswith(shortname + '/')
-
-def splitstandin(filename):
- # Split on / because that's what dirstate always uses, even on Windows.
- # Change local separator to / first just in case we are passed filenames
- # from an external source (like the command line).
- bits = util.pconvert(filename).split('/', 1)
- if len(bits) == 2 and bits[0] == shortname:
- return bits[1]
- else:
- return None
-
-def updatestandin(repo, standin):
- file = repo.wjoin(splitstandin(standin))
- if os.path.exists(file):
- hash = hashfile(file)
- executable = getexecutable(file)
- writestandin(repo, standin, hash, executable)
-
-def readstandin(repo, filename, node=None):
- '''read hex hash from standin for filename at given node, or working
- directory if no node is given'''
- return repo[node][standin(filename)].data().strip()
-
-def writestandin(repo, standin, hash, executable):
- '''write hash to <repo.root>/<standin>'''
- writehash(hash, repo.wjoin(standin), executable)
-
-def copyandhash(instream, outfile):
- '''Read bytes from instream (iterable) and write them to outfile,
- computing the SHA-1 hash of the data along the way. Close outfile
- when done and return the binary hash.'''
- hasher = util.sha1('')
- for data in instream:
- hasher.update(data)
- outfile.write(data)
-
- # Blecch: closing a file that somebody else opened is rude and
- # wrong. But it's so darn convenient and practical! After all,
- # outfile was opened just to copy and hash.
- outfile.close()
-
- return hasher.digest()
-
-def hashrepofile(repo, file):
- return hashfile(repo.wjoin(file))
-
-def hashfile(file):
- if not os.path.exists(file):
- return ''
- hasher = util.sha1('')
- fd = open(file, 'rb')
- for data in blockstream(fd):
- hasher.update(data)
- fd.close()
- return hasher.hexdigest()
-
-class limitreader(object):
- def __init__(self, f, limit):
- self.f = f
- self.limit = limit
-
- def read(self, length):
- if self.limit == 0:
- return ''
- length = length > self.limit and self.limit or length
- self.limit -= length
- return self.f.read(length)
-
- def close(self):
- pass
-
-def blockstream(infile, blocksize=128 * 1024):
- """Generator that yields blocks of data from infile and closes infile."""
- while True:
- data = infile.read(blocksize)
- if not data:
- break
- yield data
- # same blecch as copyandhash() above
- infile.close()
-
-def writehash(hash, filename, executable):
- util.makedirs(os.path.dirname(filename))
- util.writefile(filename, hash + '\n')
- os.chmod(filename, getmode(executable))
-
-def getexecutable(filename):
- mode = os.stat(filename).st_mode
- return ((mode & stat.S_IXUSR) and
- (mode & stat.S_IXGRP) and
- (mode & stat.S_IXOTH))
-
-def getmode(executable):
- if executable:
- return 0755
- else:
- return 0644
-
-def urljoin(first, second, *arg):
- def join(left, right):
- if not left.endswith('/'):
- left += '/'
- if right.startswith('/'):
- right = right[1:]
- return left + right
-
- url = join(first, second)
- for a in arg:
- url = join(url, a)
- return url
-
-def hexsha1(data):
- """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
- object data"""
- h = util.sha1()
- for chunk in util.filechunkiter(data):
- h.update(chunk)
- return h.hexdigest()
-
-def httpsendfile(ui, filename):
- return httpconnection.httpsendfile(ui, filename, 'rb')
-
-def unixpath(path):
- '''Return a version of path normalized for use with the lfdirstate.'''
- return util.pconvert(os.path.normpath(path))
-
-def islfilesrepo(repo):
- return ('largefiles' in repo.requirements and
- util.any(shortname + '/' in f[0] for f in repo.store.datafiles()))
-
-class storeprotonotcapable(Exception):
- def __init__(self, storetypes):
- self.storetypes = storetypes
-
-def getcurrentheads(repo):
- branches = repo.branchmap()
- heads = []
- for branch in branches:
- newheads = repo.branchheads(branch)
- heads = heads + newheads
- return heads
-
-def getstandinsstate(repo):
- standins = []
- matcher = getstandinmatcher(repo)
- for standin in dirstatewalk(repo.dirstate, matcher):
- lfile = splitstandin(standin)
- standins.append((lfile, readstandin(repo, lfile)))
- return standins
-
-def getlfilestoupdate(oldstandins, newstandins):
- changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
- filelist = []
- for f in changedstandins:
- if f[0] not in filelist:
- filelist.append(f[0])
- return filelist