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:
Submodules will require dvc to detect and use appropriate .dvc for each target, otherwise it will not use appropriate cache and config. I suggest we leave this feature for the next release.
Let's use `-c` \ `--cwd` as the option name - the same as `dvc run`.

If dvc-file was created outside of dvc-root dir (`dvc run` without `--cwd`) then a user should be able to reproduce this command later: `dvc repro --cwd`. 

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- 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
--- a/dvc/command/repro.py
+++ b/dvc/command/repro.py
@@ -8,6 +8,11 @@
 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 @@ def run(self):
             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 @@ def run(self):
                 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
--- a/tests/test_repro.py
+++ b/tests/test_repro.py
@@ -186,6 +186,37 @@ def test(self):
         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