Repository: iterative/dvc

Issue:
dvc repro: change dir
The same as `make -C external-lib`.
Important: Make sure it works with Git submodules!

```
$ dvc repro -C myproj
```

```
$ dvc repro -C myproj myproj/Posts.xml.dvc
```

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index 3f25a701e..92077daad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,5 @@ innosetup/config.ini
 .coverage
 
 dvc/version.py
+
+*.swp
diff --git a/dvc/cli.py b/dvc/cli.py
index 075aff008..2ba52bb3f 100644
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -215,6 +215,10 @@ def parse_args(argv=None):
                         action='store_true',
                         default=False,
                         help='Reproduce only single data item without recursive dependencies check.')
+    repro_parser.add_argument('-c',
+                        '--cwd',
+                        default=os.path.curdir,
+                        help='Directory to reproduce from.')
     repro_parser.set_defaults(func=CmdRepro)
 
     # Remove
diff --git a/dvc/command/repro.py b/dvc/command/repro.py
index 4125ddb33..681df032e 100644
--- a/dvc/command/repro.py
+++ b/dvc/command/repro.py
@@ -8,6 +8,11 @@ from dvc.stage import Stage
 class CmdRepro(CmdBase):
     def run(self):
         recursive = not self.args.single_item
+        saved_dir = os.path.realpath(os.curdir)
+        if self.args.cwd:
+            os.chdir(self.args.cwd)
+
+        ret = 0
         for target in self.args.targets:
             try:
                 self.project.reproduce(target,
@@ -16,7 +21,8 @@ class CmdRepro(CmdBase):
             except ReproductionError as ex:
                 msg = 'Failed to reproduce \'{}\''.format(target)
                 self.project.logger.error(msg, ex)
-                return 1
+                ret = 1
+                break
             except StageNotFoundError as ex:
                 if os.path.exists(target):
                     msg = '\'{}\' is not a dvc file.'.format(target)
@@ -26,9 +32,13 @@ class CmdRepro(CmdBase):
                 else:
                     msg = '\'{}\' does not exist.'.format(target)
                 self.project.logger.error(msg)
-                return 1
+                ret = 1
+                break
             except Exception as ex:
                 msg = 'Failed to reproduce \'{}\' - unexpected error'.format(target)
                 self.project.logger.error(msg, ex)
-                return 1
-        return 0
+                ret = 1
+                break
+
+        os.chdir(saved_dir)
+        return ret


Test patch:
diff --git a/tests/test_repro.py b/tests/test_repro.py
index 119b58e9a..9ee5d052e 100644
--- a/tests/test_repro.py
+++ b/tests/test_repro.py
@@ -186,6 +186,37 @@ class TestCmdRepro(TestRepro):
         self.assertNotEqual(ret, 0)
 
 
+class TestCmdReproChdir(TestDvc):
+    def test(self):
+        dname = 'dir'
+        os.mkdir(dname)
+        foo = os.path.join(dname, self.FOO)
+        bar = os.path.join(dname, self.BAR)
+        code = os.path.join(dname, self.CODE)
+        shutil.copyfile(self.FOO, foo)
+        shutil.copyfile(self.CODE, code)
+
+        ret = main(['run',
+                   '-f', 'Dvcfile',
+                   '-c', dname,
+                   '-d', self.FOO,
+                   '-o', self.BAR,
+                   'python {} {} {}'.format(self.CODE, self.FOO, self.BAR)])
+        self.assertEqual(ret, 0)
+        self.assertTrue(os.path.isfile(foo))
+        self.assertTrue(os.path.isfile(bar))
+        self.assertTrue(filecmp.cmp(foo, bar))
+
+        os.unlink(bar)
+
+        ret = main(['repro',
+                    '-c', dname])
+        self.assertEqual(ret, 0)
+        self.assertTrue(os.path.isfile(foo))
+        self.assertTrue(os.path.isfile(bar))
+        self.assertTrue(filecmp.cmp(foo, bar))
+
+
 class TestReproExternalBase(TestDvc):
     def should_test(self):
         return False