diff --git a/README.md b/README.md index 67d2c59..872d884 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ Usage usage: repofs [-h] [--hash-trees] [--no-ref-symlinks] [--no-cache] repo mount positional arguments: - repo Git repository to be processed. - mount Path where the FileSystem will be mounted.If it doesn't - exist it is created and if it exists and contains files - RepoFS exits. + uri URI of the Git repository to be processed. optional arguments: -h, --help show this help message and exit + --git-path Path where the Git repository will be cloned. + --mount-path Path where the file system will be mounted + --foreground Allow to execute the tool in foreground mode. --hash-trees Store 256 entries (first two digits) at each levelof commits-by-hash for the first three levels. --no-ref-symlinks Do not create symlinks for commits of refs. diff --git a/bin/repofs b/bin/repofs new file mode 100644 index 0000000..e478994 --- /dev/null +++ b/bin/repofs @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# Copyright 2017-2021 Vitalis Salis and Diomidis Spinellis +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import os +import argparse +import datetime +import fuse +from fuse import FUSE +from pygit2 import clone_repository, Repository, GIT_BRANCH_REMOTE, GIT_MERGE_ANALYSIS_UP_TO_DATE, GIT_MERGE_ANALYSIS_FASTFORWARD, GIT_MERGE_ANALYSIS_NORMAL + +fuse.fuse_python_api = (0, 1) + +from repofs.repofs import RepoFS + + +REPOFS_REPOS_DIR = '~/.repofs/repositories/' +REPOFS_MOUNTS_DIR = '~/.repofs/mounts/' + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("uri", help="URI of the Git repository") + parser.add_argument("--git-path", dest='git_path', help="Path where the Git repository will be cloned.") + parser.add_argument("--mount-path", dest='mount_path', help="Path where the file system will be mounted.") + + parser.add_argument("--foreground", dest='foreground', help="Allow to execute the tool in foreground mode.", action="store_true") + parser.add_argument( + "--hash-trees", + help="Store 256 entries (first two digits) at each level" \ + "of commits-by-hash for the first three levels.", + action="store_true", + default=False + ) + parser.add_argument( + "--no-ref-symlinks", + help="Do not create symlinks for commits of refs.", + action="store_true", + default=False + ) + parser.add_argument( + "--no-cache", + help="Do not use the cache", + action="store_true", + default=False + ) + args = parser.parse_args() + processed_uri = args.uri.lstrip('/') + + git_path = args.git_path + if not git_path: + git_path = os.path.join(os.path.expanduser(REPOFS_REPOS_DIR), processed_uri) + + mount_path = args.mount_path + if not mount_path: + mount_path = os.path.join(os.path.expanduser(REPOFS_MOUNTS_DIR), processed_uri) + os.makedirs(mount_path, exist_ok=True) + + if not os.path.exists(git_path): + clone_repository(args.uri, git_path) + else: + repo = Repository(git_path) + for remote in repo.remotes: + if remote.name != 'origin': + continue + + remote.fetch() + for branch in repo.raw_listall_branches(GIT_BRANCH_REMOTE): + branch = branch.decode('utf-8') + if branch.endswith('HEAD') or not branch.startswith('origin/'): + continue + + remote_id = repo.lookup_reference('refs/remotes/%s' % (branch)).target + merge_result, _ = repo.merge_analysis(remote_id) + if merge_result & GIT_MERGE_ANALYSIS_UP_TO_DATE: + continue + elif merge_result & GIT_MERGE_ANALYSIS_FASTFORWARD: + repo.checkout_tree(repo.get(remote_id)) + try: + master_ref = repo.lookup_reference('refs/heads/%s' % (branch)) + master_ref.set_target(remote_id) + except KeyError: + repo.create_branch(branch, repo.get(remote_id)) + else: + raise AssertionError('Sync failed with upstream') + + sys.stderr.write("Examining repository. Please wait..\n") + start = datetime.datetime.now() + repo = RepoFS( + repo=os.path.abspath(git_path), + mount=os.path.abspath(mount_path), + hash_trees=args.hash_trees, + no_ref_symlinks=args.no_ref_symlinks, + no_cache=args.no_cache + ) + end = datetime.datetime.now() + sys.stderr.write("Ready! Repository mounted in %s\n" % (end - start)) + sys.stderr.write("Repository %s is now visible at %s\n" % (git_path, + mount_path)) + FUSE(repo, os.path.abspath(mount_path), nothreads=True, foreground=args.foreground) + + +if __name__ == '__main__': + main() diff --git a/repofs/__main__.py b/repofs/__main__.py deleted file mode 100644 index d36a85e..0000000 --- a/repofs/__main__.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2017-2021 Vitalis Salis and Diomidis Spinellis -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import sys -import os -import argparse -import datetime -import fuse -from fuse import FUSE - -fuse.fuse_python_api = (0, 1) - -from repofs.repofs import RepoFS - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("repo", help="Git repository to be processed.") - parser.add_argument("mount", help="Path where the FileSystem will be mounted." \ - "If it doesn't exist it is created and if it exists and contains files RepoFS exits.") - parser.add_argument( - "--hash-trees", - help="Store 256 entries (first two digits) at each level" \ - "of commits-by-hash for the first three levels.", - action="store_true", - default=False - ) - parser.add_argument( - "--no-ref-symlinks", - help="Do not create symlinks for commits of refs.", - action="store_true", - default=False - ) - parser.add_argument( - "--no-cache", - help="Do not use the cache", - action="store_true", - default=False - ) - args = parser.parse_args() - - if not os.path.exists(os.path.join(args.repo, '.git')): - raise Exception("Not a git repository") - - foreground = True - if sys.argv[0].endswith("repofs"): - foreground = False - - sys.stderr.write("Examining repository. Please wait..\n") - start = datetime.datetime.now() - repo = RepoFS( - repo=os.path.abspath(args.repo), - mount=os.path.abspath(args.mount), - hash_trees=args.hash_trees, - no_ref_symlinks=args.no_ref_symlinks, - no_cache=args.no_cache - ) - end = datetime.datetime.now() - sys.stderr.write("Ready! Repository mounted in %s\n" % (end - start)) - sys.stderr.write("Repository %s is now visible at %s\n" % (args.repo, - args.mount)) - FUSE(repo, os.path.abspath(args.mount), nothreads=True, foreground=foreground) - -if __name__ == '__main__': - main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..07086ee --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +fusepy +pygit2 \ No newline at end of file diff --git a/setup.py b/setup.py index 8cb3ed9..25ecfb6 100644 --- a/setup.py +++ b/setup.py @@ -1,36 +1,25 @@ -import os +from setuptools import setup -from setuptools import setup, find_packages +with open("README.md", "r") as readme: + long_description = readme.read() -from subprocess import call -def get_long_desc(): - with open("README.md", "r") as readme: - desc = readme.read() - - return desc - - -def setup_package(): - setup( - name='repofs', - version='0.2.6', - description='File system view of git repositories', - long_description=get_long_desc(), - long_description_content_type="text/markdown", - url='https://github.com/AUEB-BALab/RepoFS', - license='Apache Software License', - packages=find_packages(), - #data_files=[('man/man1', ['repofs.1'])], - install_requires=['fusepy', 'pygit2'], - entry_points = { - 'console_scripts': [ - 'repofs=repofs.__main__:main', - ], - }, - author = 'Vitalis Salis', - author_email = 'vitsalis@gmail.com' - ) - -if __name__ == '__main__': - setup_package() +setup( + name='repofs', + version='0.2.6', + description='File system view of git repositories', + long_description=long_description, + long_description_content_type="text/markdown", + url='https://github.com/AUEB-BALab/RepoFS', + license='Apache Software License', + packages=[ + 'repofs', + 'repofs.handlers' + ], + install_requires=['fusepy', 'pygit2'], + scripts=[ + 'bin/repofs' + ], + author='Vitalis Salis', + author_email='vitsalis@gmail.com' +)