diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py --- a/rhodecode/lib/middleware/simplegit.py +++ b/rhodecode/lib/middleware/simplegit.py @@ -22,6 +22,7 @@ SimpleGit middleware for handling git protocol request (push/clone etc.) It's implemented with basic auth function """ +import os import re import logging import urlparse @@ -34,7 +35,18 @@ log = logging.getLogger(__name__) GIT_PROTO_PAT = re.compile( - r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)') + r'^/(.+)/(info/refs|info/lfs/(.+)|git-upload-pack|git-receive-pack)') +GIT_LFS_PROTO_PAT = re.compile(r'^/(.+)/(info/lfs/(.+))') + + +def default_lfs_store(): + """ + Default lfs store location, it's consistent with Mercurials large file + store which is in .cache/largefiles + """ + from rhodecode.lib.vcs.backends.git import lfs_store + user_home = os.path.expanduser("~") + return lfs_store(user_home) class SimpleGit(simplevcs.SimpleVCS): @@ -48,8 +60,53 @@ class SimpleGit(simplevcs.SimpleVCS): :param environ: environ where PATH_INFO is stored """ repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1) + # for GIT LFS, and bare format strip .git suffix from names + if repo_name.endswith('.git'): + repo_name = repo_name[:-4] return repo_name + def _get_lfs_action(self, path, request_method): + """ + return an action based on LFS requests type. + Those routes are handled inside vcsserver app. + + batch -> POST to /info/lfs/objects/batch => PUSH/PULL + batch is based on the `operation. + that could be download or upload, but those are only + instructions to fetch so we return pull always + + download -> GET to /info/lfs/{oid} => PULL + upload -> PUT to /info/lfs/{oid} => PUSH + + verification -> POST to /info/lfs/verify => PULL + + """ + + match_obj = GIT_LFS_PROTO_PAT.match(path) + _parts = match_obj.groups() + repo_name, path, operation = _parts + log.debug( + 'LFS: detecting operation based on following ' + 'data: %s, req_method:%s', _parts, request_method) + + if operation == 'verify': + return 'pull' + elif operation == 'objects/batch': + # batch sends back instructions for API to dl/upl we report it + # as pull + if request_method == 'POST': + return 'pull' + + elif operation: + # probably a OID, upload is PUT, download a GET + if request_method == 'GET': + return 'pull' + else: + return 'push' + + # if default not found require push, as action + return 'push' + _ACTION_MAPPING = { 'git-receive-pack': 'push', 'git-upload-pack': 'pull', @@ -68,6 +125,11 @@ class SimpleGit(simplevcs.SimpleVCS): query = urlparse.parse_qs(environ['QUERY_STRING']) service_cmd = query.get('service', [''])[0] return self._ACTION_MAPPING.get(service_cmd, 'pull') + + elif GIT_LFS_PROTO_PAT.match(environ['PATH_INFO']): + return self._get_lfs_action( + environ['PATH_INFO'], environ['REQUEST_METHOD']) + elif path.endswith('/git-receive-pack'): return 'push' elif path.endswith('/git-upload-pack'): @@ -82,4 +144,10 @@ class SimpleGit(simplevcs.SimpleVCS): def _create_config(self, extras, repo_name): extras['git_update_server_info'] = utils2.str2bool( rhodecode.CONFIG.get('git_update_server_info')) + + # TODO(marcink): controll this via DB settings, store and enabled-per + # repo settings + + extras['git_lfs_enabled'] = True + extras['git_lfs_store_path'] = default_lfs_store() return extras