Repository: iterative/dvc

Issue:
Use `dvc remove --purge` instead of `--outs` by default



Discussion:
Should we remove the `--purge` flag (as it would be the default) and leave just the `--outs-only`, @efiop ?
Should we clarify that this removes the files only for the "working directory" and not from the cache?

```diff
usage: dvc remove [-h] [-q | -v] [-o | -p] [-f] targets [targets ...]

- Remove outputs of DVC file.
+ Remove DVC file and all its outputs from your workspace.

positional arguments:
  targets        DVC files.

optional arguments:
  -h, --help     show this help message and exit
  -q, --quiet    Be quiet.
  -v, --verbose  Be verbose.
-  -o, --outs     Only remove DVC file outputs.(default)
+  -o, --outs     Only remove outputs from DVC files
-  -p, --purge    Remove DVC file and all its outputs.
+  -p, --purge    Remove DVC file and all its outputs. (default)
-  -f, --force    Force purge.
+  -f, --force    Force removal.
```
👍 for better explanation. May be it makes sense to keep both options for backward compatibility and place (default) after the description.

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -484,7 +484,7 @@ def parse_args(argv=None):
     repro_parser.set_defaults(func=CmdRepro)
 
     # Remove
-    REMOVE_HELP = 'Remove outputs of DVC file.'
+    REMOVE_HELP = 'Remove DVC file and all its outputs from your workspace.'
     remove_parser = subparsers.add_parser(
                         'remove',
                         parents=[parent_parser],
@@ -495,14 +495,14 @@ def parse_args(argv=None):
                         '-o',
                         '--outs',
                         action='store_true',
-                        default=True,
-                        help='Only remove DVC file outputs.(default)')
+                        default=False,
+                        help='Only remove outputs from DVC files')
     remove_parser_group.add_argument(
                         '-p',
                         '--purge',
                         action='store_true',
-                        default=False,
-                        help='Remove DVC file and all its outputs.')
+                        default=True,
+                        help='Remove DVC file and all its outputs. (default)')
     remove_parser.add_argument(
                         '-f',
                         '--force',
diff --git a/dvc/command/remove.py b/dvc/command/remove.py
--- a/dvc/command/remove.py
+++ b/dvc/command/remove.py
@@ -4,33 +4,33 @@
 
 
 class CmdRemove(CmdBase):
-    def _is_outs_only(self, target):
-        if not self.args.purge:
-            return True
-
+    def _confirm_removal(self, target):
         if self.args.force:
-            return False
+            return
 
-        msg = (
+        confirmed = self.project.prompt.prompt(
             u'Are you sure you want to remove {} with its outputs?'
             .format(target)
         )
 
-        confirmed = self.project.prompt.prompt(msg)
-
         if confirmed:
-            return False
+            return
 
         raise DvcException(
-            u'Cannot purge without a confirmation from the user.'
+            u'Cannot remove without a confirmation from the user.'
             u" Use '-f' to force."
         )
 
     def run(self):
         for target in self.args.targets:
             try:
-                outs_only = self._is_outs_only(target)
+                outs_only = self.args.outs
+
+                if not outs_only:
+                    self._confirm_removal(target)
+
                 self.project.remove(target, outs_only=outs_only)
+
             except DvcException as ex:
                 Logger.error('Failed to remove {}'.format(target), ex)
                 return 1


Test patch:
diff --git a/tests/test_remove.py b/tests/test_remove.py
--- a/tests/test_remove.py
+++ b/tests/test_remove.py
@@ -52,30 +52,29 @@ def test(self):
         self.assertEqual(len(stages), 1)
         stage = stages[0]
         self.assertTrue(stage is not None)
-        ret = main(['remove',
-                    stage.path])
+        ret = main(['remove', '--force', stage.path])
         self.assertEqual(ret, 0)
 
-        ret = main(['remove',
-                    'non-existing-dvc-file'])
+    def test_non_existent_file(self):
+        ret = main(['remove', '--force', '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])
+    @patch('dvc.command.remove.CmdRemove._confirm_removal')
+    def test_confirmation(self, mock_confirm):
+        mock_confirm.side_effect = DvcException
 
-        self.assertEqual(ret, 0)
-        self.assertFalse(os.path.exists(self.FOO))
-        self.assertFalse(os.path.exists(dvcfile))
+        dvcfile = self.dvc.add(self.FOO)[0].path
+        ret = main(['remove', dvcfile])
 
-    @patch('dvc.prompt.Prompt.prompt')
-    def test_force(self, mock_prompt):
-        mock_prompt.return_value = False
+        mock_confirm.assert_called()
+        self.assertNotEqual(ret, 0)
+        self.assertRaises(DvcException)
 
+    def test_force(self):
         dvcfile = self.dvc.add(self.FOO)[0].path
-        ret = main(['remove', '--purge', dvcfile])
+        ret = main(['remove', '--force', dvcfile])
 
-        mock_prompt.assert_called()
-        self.assertEqual(ret, 1)
+        self.assertEqual(ret, 0)
         self.assertRaises(DvcException)
+        self.assertFalse(os.path.exists(self.FOO))
+        self.assertFalse(os.path.exists(dvcfile))