internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: iterative/dvc

Issue:
Error: Unexpected error: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr
**Setup information**
DVC version: 0.19.7, Platform: Windows 10  Pro (Version 1709), method of installation: pip (global), Python 3.6.6

Hello,
I would like to upload a directory with image data to hdfs and log this step in my DVC pipline. Therefore, I ran:

```
dvc run -d .\data\phaco_segmentation\ -o hdfs://<hostname>/data/phaco_segmentation/ hdfs dfs -put ./data/phaco_segmentation /data/
```

in powershell. Output gives me following output:
```
Running command:
        hdfs dfs -put ./data/phaco_segmentation /ignition_data/
Error: Unexpected error: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr
```

Running `hdfs dfs -put` runs without any problems. May the [this flag](https://github.com/iterative/dvc/blob/ddf1839d38a507e0e2fbae0036469dfb6d2a3f36/dvc/remote/hdfs.py#L34) the possible cause of this?


Discussion:
Hi @amjadsaadeh !

Good catch! Thank you for investigating! Preparing a patch right now.

Thanks,
Ruslan

Resolving patch:
diff --git a/dvc/remote/hdfs.py b/dvc/remote/hdfs.py
--- a/dvc/remote/hdfs.py
+++ b/dvc/remote/hdfs.py
@@ -29,9 +29,14 @@ def hadoop_fs(self, cmd, user=None):
         cmd = 'hadoop fs -' + cmd
         if user:
             cmd = 'HADOOP_USER_NAME={} '.format(user) + cmd
+
+        # NOTE: close_fds doesn't work with redirected stdin/stdout/stderr.
+        # See https://github.com/iterative/dvc/issues/1197.
+        close_fds = (os.name != 'nt')
+
         p = Popen(cmd,
                   shell=True,
-                  close_fds=True,
+                  close_fds=close_fds,
                   executable=os.getenv('SHELL'),
                   env=fix_env(os.environ),
                   stdin=PIPE,


Test patch:
Repository: iterative/dvc

Issue:
specify supported schema in dvc import help
Specify that URL is one of the supported schemas - S3, SSH, etc

```  
 usage: dvc import [-h] [-q] [-v] url out
    
    positional arguments:
      url            URL
      out            Output
    
    optional arguments:
      -h, --help     show this help message and exit
      -q, --quiet    Be quiet.
      -v, --verbose  Be verbose.
```



Discussion:

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -160,7 +160,15 @@ def parse_args(argv=None):
                         help=IMPORT_HELP)
     import_parser.add_argument(
                         'url',
-                        help='URL.')
+                        help="URL. Supported urls: "
+                             "'/path/to/file', "
+                             "'C:\\\\path\\to\\file', "
+                             "'s3://bucket/path/to/file', "
+                             "'gs://bucket/path/to/file', "
+                             "'hdfs://example.com/path/to/file', "
+                             "'ssh://example.com:/path/to/file', "
+                             "'remote://myremote/path/to/file'(see "
+                             "`dvc remote` commands). ")
     import_parser.add_argument(
                         'out',
                         help='Output.')


Test patch:
Repository: iterative/dvc

Issue:
Error: Unexpected error: cannot import name 'POINTER'
DVC version: 0.19.10+c6811a, Platform: Windows 10 Pro (Version 1709), method of installation: pip (global), Python 3.6.6

Hello,
I run the following command:

```
dvc run -d .\data\phaco_segmentation\ -o hdfs://<hostname>/data/phaco_segmentation/ hdfs dfs -put ./data/phaco_segmentation /data/
```
in powershell and get the following error message:

```
Error: Unexpected error: cannot import name 'POINTER'
```

I already got at fix for this problem. Pull request is incoming.



Discussion:

Resolving patch:
diff --git a/dvc/system.py b/dvc/system.py
--- a/dvc/system.py
+++ b/dvc/system.py
@@ -112,8 +112,8 @@ def reflink(source, link_name):
     @staticmethod
     def getdirinfo(path):
         import ctypes
-        from ctypes import c_void_p, c_wchar_p, Structure, WinError
-        from ctypes.wintypes import DWORD, HANDLE, POINTER, BOOL
+        from ctypes import c_void_p, c_wchar_p, Structure, WinError, POINTER
+        from ctypes.wintypes import DWORD, HANDLE, BOOL
 
         FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
         FILE_SHARE_READ = 0x00000001


Test patch:
Repository: iterative/dvc

Issue:
install: add pre-commit hook with `dvc status`



Discussion:

Resolving patch:
diff --git a/dvc/scm.py b/dvc/scm.py
--- a/dvc/scm.py
+++ b/dvc/scm.py
@@ -240,18 +240,22 @@ def list_branches(self):
     def list_tags(self):
         return [t.name for t in self.repo.tags]
 
-    def install(self):
+    def _install_hook(self, name, cmd):
         hook = os.path.join(self.root_dir,
                             self.GIT_DIR,
                             'hooks',
-                            'post-checkout')
+                            name)
         if os.path.isfile(hook):
             msg = 'Git hook \'{}\' already exists.'
             raise SCMError(msg.format(os.path.relpath(hook)))
         with open(hook, 'w+') as fd:
-            fd.write('#!/bin/sh\nexec dvc checkout\n')
+            fd.write('#!/bin/sh\nexec dvc {}\n'.format(cmd))
         os.chmod(hook, 0o777)
 
+    def install(self):
+        self._install_hook('post-checkout', 'checkout')
+        self._install_hook('pre-commit', 'status')
+
 
 def SCM(root_dir, no_scm=False, project=None):
     if Git.is_repo(root_dir) or Git.is_submodule(root_dir):


Test patch:
diff --git a/tests/test_install.py b/tests/test_install.py
--- a/tests/test_install.py
+++ b/tests/test_install.py
@@ -13,7 +13,11 @@ def test(self):
         ret = main(['install'])
         self.assertNotEqual(ret, 0)
 
-        self.assertTrue(os.path.isfile('.git/hooks/post-checkout'))
+        def hook(name):
+            return os.path.join('.git', 'hooks', name)
+
+        self.assertTrue(os.path.isfile(hook('post-checkout')))
+        self.assertTrue(os.path.isfile(hook('pre-commit')))
 
         self.dvc.add(self.FOO)
         os.unlink(self.FOO)
Repository: iterative/dvc

Issue:
run: Check whether file specified by the output already exists
DVC version: `0.19.12`
Installation: `Arch Linux AUR package`

This prevents overwritting the file or causing some unexpected behaviour.

```bash
#!/bin/bash

# Clean and create a repository
rm -rf myrepo
mkdir myrepo
cd myrepo

# Init git and dvc
git init
dvc init
git add -A
git commit -m "Init"

# Creating the dependency
echo "hello" > hello.txt

dvc run \
  --file circular-dependency.dvc \
  --deps hello.txt \
  --outs hello.txt \
  'echo "overwrite" > hello.txt'
```


Discussion:

Resolving patch:
diff --git a/dvc/exceptions.py b/dvc/exceptions.py
--- a/dvc/exceptions.py
+++ b/dvc/exceptions.py
@@ -31,6 +31,13 @@ def __init__(self, output, stages):
         super(OutputDuplicationError, self).__init__(msg.format(output, s))
 
 
+class CircularDependencyError(DvcException):
+    def __init__(self, dependency):
+        assert isinstance(dependency, str)
+        msg = "File '{}' is specified as an output and as a dependency."
+        super(CircularDependencyError, self).__init__(msg.format(dependency))
+
+
 class MoveNotDataSourceError(DvcException):
     def __init__(self, path):
         msg = "Move is not permitted for stages that are not data sources. " \
diff --git a/dvc/project.py b/dvc/project.py
--- a/dvc/project.py
+++ b/dvc/project.py
@@ -143,6 +143,14 @@ def _check_output_duplication(self, outs):
                         stages = [o.stage.relpath, out.stage.relpath]
                         raise OutputDuplicationError(o.path, stages)
 
+    def _check_circular_dependency(self, deps, outs):
+        from dvc.exceptions import CircularDependencyError
+
+        circular_dependencies = set(deps) & set(outs)
+
+        if circular_dependencies:
+            raise CircularDependencyError(circular_dependencies.pop())
+
     def add(self, fname, recursive=False):
         fnames = []
         if recursive and os.path.isdir(fname):
@@ -258,6 +266,7 @@ def run(self,
                             overwrite=overwrite)
 
         self._check_output_duplication(stage.outs)
+        self._check_circular_dependency(deps, outs)
 
         self._files_to_git_add = []
         with self.state:


Test patch:
diff --git a/tests/test_run.py b/tests/test_run.py
--- a/tests/test_run.py
+++ b/tests/test_run.py
@@ -5,7 +5,7 @@
 from dvc.utils import file_md5
 from dvc.stage import Stage, StageFileBadNameError, MissingDep
 from dvc.command.run import CmdRun
-from dvc.exceptions import OutputDuplicationError
+from dvc.exceptions import OutputDuplicationError, CircularDependencyError
 
 from tests.basic_env import TestDvc
 
@@ -93,6 +93,15 @@ def test(self):
         self.assertFalse(os.path.exists('out'))
 
 
+class TestRunCircularDependency(TestDvc):
+    def test(self):
+        with self.assertRaises(CircularDependencyError):
+            self.dvc.run(cmd='',
+                         deps=[self.FOO],
+                         outs=[self.FOO],
+                         fname='circular-dependency.dvc')
+
+
 class TestCmdRun(TestDvc):
     def test_run(self):
         ret = main(['run',
Repository: iterative/dvc

Issue:
Don't require a lock for the dvc root command
Is there any way that the `dvc root` would change in the middle of some `dvc` command that acquired lock? If not, I think `dvc root` could simply skip acquiring a lock so it can be used freely.


Discussion:

Resolving patch:
diff --git a/dvc/command/root.py b/dvc/command/root.py
--- a/dvc/command/root.py
+++ b/dvc/command/root.py
@@ -4,6 +4,9 @@
 
 
 class CmdRoot(CmdBase):
+    def run_cmd(self):
+        return self.run()
+
     def run(self):
         self.project.logger.info(os.path.relpath(self.project.root_dir))
         return 0


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -28,6 +28,11 @@ def test_root(self):
         ret = main(['root'])
         self.assertEqual(ret, 0)
 
+        # NOTE: check that `dvc root` is not blocked with dvc lock
+        with self.dvc.lock:
+            ret = main(['root'])
+        self.assertEqual(ret, 0)
+
     def _do_test(self, local=False):
         section = 'setsection'
         field = 'setfield'
Repository: iterative/dvc

Issue:
remove -p: add a confirmation prompt
E.g. see `dvc/prompt.py`.


Discussion:

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -501,6 +501,12 @@ def parse_args(argv=None):
                         action='store_true',
                         default=False,
                         help='Remove DVC file and all its outputs.')
+    remove_parser.add_argument(
+                        '-f',
+                        '--force',
+                        action='store_true',
+                        default=False,
+                        help='Force purge.')
     remove_parser.add_argument(
                         'targets',
                         nargs='+',
diff --git a/dvc/command/remove.py b/dvc/command/remove.py
--- a/dvc/command/remove.py
+++ b/dvc/command/remove.py
@@ -4,10 +4,32 @@
 
 
 class CmdRemove(CmdBase):
+    def _is_outs_only(self, target):
+        if not self.args.purge:
+            return True
+
+        if self.args.force:
+            return False
+
+        msg = (
+            u'Are you sure you want to remove {} with its outputs?'
+            .format(target)
+        )
+
+        confirmed = self.project.prompt.prompt(msg)
+
+        if confirmed:
+            return False
+
+        raise DvcException(
+            u'Cannot purge without a confirmation from the user.'
+            u" Use '-f' to force."
+        )
+
     def run(self):
-        outs_only = not self.args.purge
         for target in self.args.targets:
             try:
+                outs_only = self._is_outs_only(target)
                 self.project.remove(target, outs_only=outs_only)
             except DvcException as ex:
                 Logger.error('Failed to remove {}'.format(target), ex)


Test patch:
diff --git a/tests/test_remove.py b/tests/test_remove.py
--- a/tests/test_remove.py
+++ b/tests/test_remove.py
@@ -2,9 +2,11 @@
 
 from dvc.main import main
 from dvc.stage import Stage, StageFileDoesNotExistError
+from dvc.exceptions import DvcException
 from dvc.command.remove import CmdRemove
 
 from tests.basic_env import TestDvc
+from mock import patch
 
 
 class TestRemove(TestDvc):
@@ -57,3 +59,23 @@ def test(self):
         ret = main(['remove',
                     'non-existing-dvc-file'])
         self.assertNotEqual(ret, 0)
+
+class TestRemovePurge(TestDvc):
+    def test(self):
+        dvcfile = self.dvc.add(self.FOO)[0].path
+        ret = main(['remove', '--purge', '--force', dvcfile])
+
+        self.assertEqual(ret, 0)
+        self.assertFalse(os.path.exists(self.FOO))
+        self.assertFalse(os.path.exists(dvcfile))
+
+    @patch('dvc.prompt.Prompt.prompt')
+    def test_force(self, mock_prompt):
+        mock_prompt.return_value = False
+
+        dvcfile = self.dvc.add(self.FOO)[0].path
+        ret = main(['remove', '--purge', dvcfile])
+
+        mock_prompt.assert_called()
+        self.assertEqual(ret, 1)
+        self.assertRaises(DvcException)
Repository: iterative/dvc

Issue:
cli: Move mutual exclusion of --quiet and --verbose to the parser level



Discussion:

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -56,13 +56,15 @@ def __call__(self, parser, namespace, values, option_string=None):
 def parse_args(argv=None):
     # Common args
     parent_parser = argparse.ArgumentParser(add_help=False)
-    parent_parser.add_argument(
+
+    log_level_group = parent_parser.add_mutually_exclusive_group()
+    log_level_group.add_argument(
                         '-q',
                         '--quiet',
                         action='store_true',
                         default=False,
                         help='Be quiet.')
-    parent_parser.add_argument(
+    log_level_group.add_argument(
                         '-v',
                         '--verbose',
                         action='store_true',
diff --git a/dvc/command/base.py b/dvc/command/base.py
--- a/dvc/command/base.py
+++ b/dvc/command/base.py
@@ -15,9 +15,10 @@ def __init__(self, args):
 
     @staticmethod
     def _set_loglevel(args):
-        if args.quiet and not args.verbose:
+        if args.quiet:
             Logger.be_quiet()
-        elif not args.quiet and args.verbose:
+
+        elif args.verbose:
             Logger.be_verbose()
 
     def _find_root(self):


Test patch:
Repository: iterative/dvc

Issue:
run: Check whether file specified by the output already exists
DVC version: `0.19.12`
Installation: `Arch Linux AUR package`

This prevents overwritting the file or causing some unexpected behaviour.

```bash
#!/bin/bash

# Clean and create a repository
rm -rf myrepo
mkdir myrepo
cd myrepo

# Init git and dvc
git init
dvc init
git add -A
git commit -m "Init"

# Creating the dependency
echo "hello" > hello.txt

dvc run \
  --file circular-dependency.dvc \
  --deps hello.txt \
  --outs hello.txt \
  'echo "overwrite" > hello.txt'
```


Discussion:
Btw, you can still get this bug if you use `-O|--outs-no-cache`:

```
dvc run \
  --file circular-dependency.dvc \
  --deps hello.txt \
  --outs-no-cache hello.txt \
  'echo "overwrite" > hello.txt'
```

This is because you should check `stage.outs` and `stage.deps`, instead of `outs` and `deps`. Plus, when you are using `outs` and `deps`, they can have non-normalized paths e.g. `./foo` and `foo`. You should use `.path` property for every entry in `stage.outs` and `stage.deps` lists. Could you please send a fix for that?

Thanks,
Ruslan
Sure, @efiop! I'll send a patch tomorrow

Resolving patch:
diff --git a/dvc/project.py b/dvc/project.py
--- a/dvc/project.py
+++ b/dvc/project.py
@@ -146,7 +146,10 @@ def _check_output_duplication(self, outs):
     def _check_circular_dependency(self, deps, outs):
         from dvc.exceptions import CircularDependencyError
 
-        circular_dependencies = set(deps) & set(outs)
+        circular_dependencies = (
+            set(file.path for file in deps) &
+            set(file.path for file in outs)
+        )
 
         if circular_dependencies:
             raise CircularDependencyError(circular_dependencies.pop())
@@ -298,7 +301,7 @@ def run(self,
                             overwrite=overwrite)
 
         self._check_output_duplication(stage.outs)
-        self._check_circular_dependency(deps, outs)
+        self._check_circular_dependency(stage.deps, stage.outs)
 
         self._files_to_git_add = []
         with self.state:


Test patch:
diff --git a/tests/test_run.py b/tests/test_run.py
--- a/tests/test_run.py
+++ b/tests/test_run.py
@@ -102,6 +102,21 @@ def test(self):
                          outs=[self.FOO],
                          fname='circular-dependency.dvc')
 
+    def test_outs_no_cache(self):
+        with self.assertRaises(CircularDependencyError):
+            self.dvc.run(cmd='',
+                         deps=[self.FOO],
+                         outs_no_cache=[self.FOO],
+                         fname='circular-dependency.dvc')
+
+    def test_non_normalized_paths(self):
+        with self.assertRaises(CircularDependencyError):
+            self.dvc.run(cmd='',
+                         deps=['./foo'],
+                         outs=['foo'],
+                         fname='circular-dependency.dvc')
+
+
 class TestRunBadCwd(TestDvc):
     def test(self):
         with self.assertRaises(StageBadCwdError):
Repository: iterative/dvc

Issue:
introduce `dvc pl list`
E.g.

```
1.dvc
2.dvc
...
1000.dvc
===============
isolated1.dvc
===============
isolated2.dvc

3 pipelines total 
```


Discussion:

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -25,7 +25,7 @@
 from dvc.command.install import CmdInstall
 from dvc.command.root import CmdRoot
 from dvc.command.lock import CmdLock, CmdUnlock
-from dvc.command.pipeline import CmdPipelineShow
+from dvc.command.pipeline import CmdPipelineShow, CmdPipelineList
 from dvc.logger import Logger
 from dvc import VERSION
 
@@ -927,6 +927,14 @@ def parse_args(argv=None):
                         help="DVC files. 'Dvcfile' by default.")
     pipeline_show_parser.set_defaults(func=CmdPipelineShow)
 
+    PIPELINE_LIST_HELP = 'List pipelines.'
+    pipeline_list_parser = pipeline_subparsers.add_parser(
+                        'list',
+                        parents=[parent_parser],
+                        description=PIPELINE_LIST_HELP,
+                        help=PIPELINE_LIST_HELP)
+    pipeline_list_parser.set_defaults(func=CmdPipelineList)
+
     args = parser.parse_args(argv)
 
     if (issubclass(args.func, CmdRepro)
diff --git a/dvc/command/pipeline.py b/dvc/command/pipeline.py
--- a/dvc/command/pipeline.py
+++ b/dvc/command/pipeline.py
@@ -110,3 +110,19 @@ def run(self, unlock=False):
                 self.project.logger.error(msg, ex)
                 return 1
         return 0
+
+
+class CmdPipelineList(CmdBase):
+    def run(self):
+        import networkx
+
+        pipelines = self.project.pipelines()
+        for p in pipelines:
+            stages = networkx.get_node_attributes(p, 'stage')
+            for stage in stages:
+                self.project.logger.info(stage)
+            if len(stages) != 0:
+                self.project.logger.info("="*80)
+        self.project.logger.info("{} pipeline(s) total".format(len(pipelines)))
+
+        return 0


Test patch:
diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py
--- a/tests/test_pipeline.py
+++ b/tests/test_pipeline.py
@@ -165,3 +165,15 @@ def test_not_dvc_file(self):
     def test_non_existing(self):
         ret = main(['pipeline', 'show', 'non-existing'])
         self.assertNotEqual(ret, 0)
+
+
+class TestPipelineListEmpty(TestDvc):
+    def test(self):
+        ret = main(['pipeline', 'list'])
+        self.assertEqual(ret, 0)
+
+
+class TestPipelineListSingle(TestPipelineShowDeep):
+    def test(self):
+        ret = main(['pipeline', 'list'])
+        self.assertEqual(ret, 0)
Repository: iterative/dvc

Issue:
dvc: add sqlite temp files to gitignore
E.g. .dvc/state-journal. See https://www.sqlite.org/tempfiles.html for other temporary files


Discussion:

Resolving patch:
diff --git a/dvc/project.py b/dvc/project.py
--- a/dvc/project.py
+++ b/dvc/project.py
@@ -131,10 +131,12 @@ def destroy(self):
         shutil.rmtree(self.dvc_dir)
 
     def _ignore(self):
-        flist = [self.state.state_file,
-                 self.lock.lock_file,
-                 self.config.config_local_file,
-                 self.updater.updater_file]
+        flist = [
+            self.state.state_file,
+            self.lock.lock_file,
+            self.config.config_local_file,
+            self.updater.updater_file,
+        ] + self.state.temp_files
 
         if self.cache.local.cache_dir.startswith(self.root_dir):
             flist += [self.cache.local.cache_dir]
diff --git a/dvc/state.py b/dvc/state.py
--- a/dvc/state.py
+++ b/dvc/state.py
@@ -57,6 +57,13 @@ def __init__(self, project, config):
             return
 
         self.state_file = os.path.join(self.dvc_dir, self.STATE_FILE)
+
+        # https://www.sqlite.org/tempfiles.html
+        self.temp_files = [
+            self.state_file + '-journal',
+            self.state_file + '-wal',
+        ]
+
         self.db = None
         self.c = None
         self.inserts = 0


Test patch:
Repository: iterative/dvc

Issue:
gitignore: use unambiguous paths
E.g. `/dir` instead of `dir`.


Discussion:

Resolving patch:
diff --git a/dvc/scm.py b/dvc/scm.py
--- a/dvc/scm.py
+++ b/dvc/scm.py
@@ -151,7 +151,8 @@ def ignore_file(self):
 
     def _get_gitignore(self, path):
         assert os.path.isabs(path)
-        entry = os.path.basename(path)
+        # NOTE: using '/' prefix to make path unambiguous
+        entry = '/' + os.path.basename(path)
         gitignore = os.path.join(os.path.dirname(path), self.GITIGNORE)
 
         if not gitignore.startswith(self.root_dir):


Test patch:
diff --git a/tests/test_checkout.py b/tests/test_checkout.py
--- a/tests/test_checkout.py
+++ b/tests/test_checkout.py
@@ -205,8 +205,8 @@ def test(self):
 
         self.assertEqual(len(ignored), 2)
 
-        self.assertIn(fname1, ignored)
-        self.assertIn(fname2, ignored)
+        self.assertIn('/' + fname1, ignored)
+        self.assertIn('/' + fname2, ignored)
 
 
 class TestGitIgnoreWhenCheckout(CheckoutBase):
@@ -232,13 +232,13 @@ def test(self):
         ignored = self.read_ignored()
 
         self.assertEqual(len(ignored), 1)
-        self.assertIn(fname_master, ignored)
+        self.assertIn('/' + fname_master, ignored)
 
         self.dvc.scm.checkout(branch_1)
         ret = main(['checkout'])
         self.assertEqual(ret, 0)
         ignored = self.read_ignored()
-        self.assertIn(fname_branch, ignored)
+        self.assertIn('/' + fname_branch, ignored)
 
 
 class TestCheckoutMissingMd5InStageFile(TestRepro):
diff --git a/tests/test_scm.py b/tests/test_scm.py
--- a/tests/test_scm.py
+++ b/tests/test_scm.py
@@ -45,7 +45,8 @@ class TestIgnore(TestGit):
     def _count_gitignore(self):
         with open(Git.GITIGNORE, 'r') as fd:
             lines = fd.readlines()
-            return len(list(filter(lambda x: x.strip() == 'foo', lines)))
+            return len(list(filter(lambda x: x.strip() == '/' + self.FOO,
+                                   lines)))
 
     def test(self):
         git = Git(self._root_dir)