diff --git a/dvc/cli.py b/dvc/cli.py index 405bb5f96..cf1cd99c0 100644 --- a/dvc/cli.py +++ b/dvc/cli.py @@ -18,6 +18,7 @@ from dvc.command.config import CmdConfig from dvc.command.checkout import CmdCheckout from dvc.command.remote import CmdRemoteAdd, CmdRemoteRemove, CmdRemoteModify from dvc.command.metrics import CmdMetrics +from dvc.command.install import CmdInstall from dvc.stage import Stage from dvc import VERSION @@ -341,6 +342,13 @@ def parse_args(argv=None): help='Headed TSV path \'row,column\'(e.g. \'Name,3\'') metrics_parser.set_defaults(func=CmdMetrics) + # Install + install_parser = subparsers.add_parser( + 'install', + parents=[parent_parser], + help='Install dvc hooks into the repository') + install_parser.set_defaults(func=CmdInstall) + args = parser.parse_args(argv) return args diff --git a/dvc/command/install.py b/dvc/command/install.py new file mode 100644 index 000000000..f6dd1a78c --- /dev/null +++ b/dvc/command/install.py @@ -0,0 +1,13 @@ +from dvc.project import Project +from dvc.logger import Logger +from dvc.command.common.base import CmdBase + + +class CmdInstall(CmdBase): + def run_cmd(self): + try: + self.project.install() + except Exception as e: + Logger.error('Failed to install dvc hooks', e) + return 1 + return 0 diff --git a/dvc/project.py b/dvc/project.py index a56ba406c..070a8c370 100644 --- a/dvc/project.py +++ b/dvc/project.py @@ -91,6 +91,9 @@ class Project(object): return Project(root_dir) + def install(self): + self.scm.install() + def to_dvc_path(self, path): return os.path.relpath(path, self.root_dir) diff --git a/dvc/scm.py b/dvc/scm.py index 3c4755bfe..606359c02 100644 --- a/dvc/scm.py +++ b/dvc/scm.py @@ -55,6 +55,9 @@ class Base(object): def list_branches(self): pass + def install(self): + pass + class Git(Base): GITIGNORE = '.gitignore' @@ -145,6 +148,14 @@ class Git(Base): def list_branches(self): return [h.name for h in self.repo.heads] + def install(self): + hook = os.path.join(self.root_dir, self.GIT_DIR, 'hooks', 'post-checkout') + if os.path.isfile(hook): + raise SCMError('Git hook \'{}\' already exists.'.format(os.path.relpath(hook))) + with open(hook, 'w+') as fd: + fd.write('#!/bin/sh\nexec dvc checkout\n') + os.chmod(hook, 0o777) + def SCM(root_dir=os.curdir, no_scm=False): if Git.is_repo(root_dir): diff --git a/tests/__main__.py b/tests/__main__.py index f71406278..16bdcafd6 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -4,4 +4,7 @@ from subprocess import check_call REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) os.chdir(REPO_ROOT) +os.putenv('PATH', '{}:{}'.format(os.path.join(REPO_ROOT, 'bin'), os.getenv('PATH'))) +os.putenv('DVC_HOME', REPO_ROOT) + check_call('nosetests --cover-inclusive --cover-erase --cover-package=dvc --with-coverage', shell=True) diff --git a/tests/test_install.py b/tests/test_install.py new file mode 100644 index 000000000..d235bbb05 --- /dev/null +++ b/tests/test_install.py @@ -0,0 +1,19 @@ +import os + +from dvc.main import main + +from tests.basic_env import TestDvc + + +class TestInit(TestDvc): + def test(self): + ret = main(['install']) + self.assertEqual(ret, 0) + + self.assertTrue(os.path.isfile('.git/hooks/post-checkout')) + + self.dvc.add(self.FOO) + os.unlink(self.FOO) + + self.dvc.scm.checkout('branch', create_new=True) + self.assertTrue(os.path.isfile(self.FOO))