Repository: pypa/pip
Pull request #13365 (2025-04-29 23:17:36)

Consistently pass verbosity flag to VCS module
Implements https://github.com/pypa/pip/issues/13329

[As suggested by @uranusjr](https://github.com/pypa/pip/issues/13329#issuecomment-2804156394), I've adopted the interface of the already existing cascade for verbosity flagging.

Even though my feature request is only applicable to the Git VCS module, the interface of the super class forced me to also implement the verbosity flagging for all other subclasses.

Since the feature is applicable to subprocesses calls, which only causes changes to their *stdout*, ~~I did not deem it feasible to write specific unit tests for this feature.~~

ByteB4rb1e: I'm currently trying to isolate the failing test-cases locally. I'm executing `nox` in a *venv* but haven't yet gotten to the point on how to pass extra arguments to the test session's `pytest` invocation.

Will resume in the next couple of days, first checking the expected output of the test cases, then figuring out what my implementation has changed. I doubt/hope it's anything major.
ichard26: @ByteB4rb1e You can pass extra arguments to the pytest invocation by adding `--` followed by the extra arguments. For example:

```
nox -s test-3.12 -- tests/functional --collect-only
```

This is also covered in the pip contributing documentation: https://pip.pypa.io/en/stable/development/getting-started/#running-tests
ByteB4rb1e: One more thing that came to mind as I couldn't find a definitive requirement in the *Development - Getting Started* guide: Should the news fragment reference the issue the PR resolves, or the PR itself? I currently am referencing the issue it resolves.
ByteB4rb1e: Pardon... Had to rebase as I wasn't aware that GitHub does a merge commit when "syncing" with the upstream onto a differently named branch...
ByteB4rb1e: > Would it be a good idea if we add some mock-based tests so we are more confident what args are passed to the underlying module in what cases?

Yes, definetly! Testing the interface at least makes sense...

I still have to reformat the test cases, but ran into two issues:

1. I'm unable to get the result I'm expecting when calling the `src/pip/_internal/vcs/git.py/Git::update_submodules` method from within any of the methods defined by the super class (`update`, `fetch_new`). - See https://github.com/pypa/pip/pull/13365/commits/5b863dab90c513c97d274cb4542b3750508f7b6e, for more information. I will have to further investigate as to why...

2. For some reason I'm unable to get the method signatures to update when running `pytest`. I'm receiving `TypeError`s saying that `verbosity` is an unexpected keyword argument. The arguments are defined for all applicable methods, yet when I debug with `print(help(self.svn))` within the test cases, I can see that the instances don't have the argument defined...
ByteB4rb1e: The test case executions look better than I expected. All of this is redeemable. Though I can't debug locally with my current setup, as the results look vastly different... And I wasn't able to execute the failing tests properly yet. I don't quite understand why `pytest` executes differently on my local machine, regardless of using `nox` as a wrapper, or not... Is there some `pytest` plugin that I'm maybe unaware of checking out a specific revision?

Patch:
diff --git a/news/13329.feature.rst b/news/13329.feature.rst
new file mode 100644
index 000000000..fa678792d
--- /dev/null
+++ b/news/13329.feature.rst
@@ -0,0 +1,1 @@
+consistently pass verbosity flag to VCS module
diff --git a/src/pip/_internal/vcs/bazaar.py b/src/pip/_internal/vcs/bazaar.py
index c0fd26e03..3a8a21e62 100644
--- a/src/pip/_internal/vcs/bazaar.py
+++ b/src/pip/_internal/vcs/bazaar.py
@@ -55,20 +55,37 @@ class Bazaar(VersionControl):
         )
         self.run_command(cmd_args)
 
-    def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
         self.run_command(make_command("switch", url), cwd=dest)
 
-    def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        flags = []
+
+        if verbosity <= 0:
+            flags.append("-q")
+
         output = self.run_command(
             make_command("info"), show_stdout=False, stdout_only=True, cwd=dest
         )
         if output.startswith("Standalone "):
             # Older versions of pip used to create standalone branches.
             # Convert the standalone branch to a checkout by calling "bzr bind".
-            cmd_args = make_command("bind", "-q", url)
+            cmd_args = make_command("bind", *flags, url)
             self.run_command(cmd_args, cwd=dest)
 
-        cmd_args = make_command("update", "-q", rev_options.to_args())
+        cmd_args = make_command("update", *flags, rev_options.to_args())
         self.run_command(cmd_args, cwd=dest)
 
     @classmethod
diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py
index f25249307..34fcf9713 100644
--- a/src/pip/_internal/vcs/git.py
+++ b/src/pip/_internal/vcs/git.py
@@ -331,31 +331,59 @@ class Git(VersionControl):
         logger.info("Resolved %s to commit %s", url, rev_options.rev)
 
         #: repo may contain submodules
-        self.update_submodules(dest)
-
-    def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+        self.update_submodules(dest, verbosity=verbosity)
+
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
         self.run_command(
             make_command("config", "remote.origin.url", url),
             cwd=dest,
         )
-        cmd_args = make_command("checkout", "-q", rev_options.to_args())
+
+        extra_flags = []
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
+        cmd_args = make_command("checkout", *extra_flags, rev_options.to_args())
         self.run_command(cmd_args, cwd=dest)
 
-        self.update_submodules(dest)
+        self.update_submodules(dest, verbosity=verbosity)
+
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        extra_flags = []
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
 
-    def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
         # First fetch changes from the default remote
         if self.get_git_version() >= (1, 9):
             # fetch tags in addition to everything else
-            self.run_command(["fetch", "-q", "--tags"], cwd=dest)
+            self.run_command(["fetch", "--tags", *extra_flags], cwd=dest)
         else:
-            self.run_command(["fetch", "-q"], cwd=dest)
+            self.run_command(["fetch", *extra_flags], cwd=dest)
         # Then reset to wanted revision (maybe even origin/master)
         rev_options = self.resolve_revision(dest, url, rev_options)
-        cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args())
+        cmd_args = make_command(
+            "reset",
+            "--hard",
+            *extra_flags,
+            rev_options.to_args(),
+        )
         self.run_command(cmd_args, cwd=dest)
         #: update submodules
-        self.update_submodules(dest)
+        self.update_submodules(dest, verbosity=verbosity)
 
     @classmethod
     def get_remote_url(cls, location: str) -> str:
@@ -496,11 +524,16 @@ class Git(VersionControl):
         return url, rev, user_pass
 
     @classmethod
-    def update_submodules(cls, location: str) -> None:
+    def update_submodules(cls, location: str, verbosity: int = 0) -> None:
+        argv = ["submodule", "update", "--init", "--recursive"]
+
+        if verbosity <= 0:
+            argv.append("-q")
+
         if not os.path.exists(os.path.join(location, ".gitmodules")):
             return
         cls.run_command(
-            ["submodule", "update", "--init", "--recursive", "-q"],
+            argv,
             cwd=location,
         )
 
diff --git a/src/pip/_internal/vcs/mercurial.py b/src/pip/_internal/vcs/mercurial.py
index 6d779837b..c87580316 100644
--- a/src/pip/_internal/vcs/mercurial.py
+++ b/src/pip/_internal/vcs/mercurial.py
@@ -58,9 +58,20 @@ class Mercurial(VersionControl):
             cwd=dest,
         )
 
-    def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        extra_flags = []
         repo_config = os.path.join(dest, self.dirname, "hgrc")
         config = configparser.RawConfigParser()
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
         try:
             config.read(repo_config)
             config.set("paths", "default", url.secret)
@@ -69,12 +80,23 @@ class Mercurial(VersionControl):
         except (OSError, configparser.NoSectionError) as exc:
             logger.warning("Could not switch Mercurial repository to %s: %s", url, exc)
         else:
-            cmd_args = make_command("update", "-q", rev_options.to_args())
+            cmd_args = make_command("update", *extra_flags, rev_options.to_args())
             self.run_command(cmd_args, cwd=dest)
 
-    def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
-        self.run_command(["pull", "-q"], cwd=dest)
-        cmd_args = make_command("update", "-q", rev_options.to_args())
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        extra_flags = []
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
+        self.run_command(["pull", *extra_flags], cwd=dest)
+        cmd_args = make_command("update", *extra_flags, rev_options.to_args())
         self.run_command(cmd_args, cwd=dest)
 
     @classmethod
diff --git a/src/pip/_internal/vcs/subversion.py b/src/pip/_internal/vcs/subversion.py
index 3679a17ac..f245eed58 100644
--- a/src/pip/_internal/vcs/subversion.py
+++ b/src/pip/_internal/vcs/subversion.py
@@ -300,7 +300,13 @@ class Subversion(VersionControl):
         )
         self.run_command(cmd_args)
 
-    def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
         cmd_args = make_command(
             "switch",
             self.get_remote_call_options(),
@@ -310,7 +316,13 @@ class Subversion(VersionControl):
         )
         self.run_command(cmd_args)
 
-    def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
         cmd_args = make_command(
             "update",
             self.get_remote_call_options(),
diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py
index 189da2197..4e91ccd4c 100644
--- a/src/pip/_internal/vcs/versioncontrol.py
+++ b/src/pip/_internal/vcs/versioncontrol.py
@@ -451,7 +451,13 @@ class VersionControl:
         """
         raise NotImplementedError
 
-    def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
         """
         Switch the repo at ``dest`` to point to ``URL``.
 
@@ -460,7 +466,13 @@ class VersionControl:
         """
         raise NotImplementedError
 
-    def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
         """
         Update an already-existing repo to the given ``rev_options``.
 
@@ -512,7 +524,7 @@ class VersionControl:
                         self.repo_name,
                         rev_display,
                     )
-                    self.update(dest, url, rev_options)
+                    self.update(dest, url, rev_options, verbosity=verbosity)
                 else:
                     logger.info("Skipping because already up-to-date.")
                 return
@@ -567,7 +579,7 @@ class VersionControl:
                 url,
                 rev_display,
             )
-            self.switch(dest, url, rev_options)
+            self.switch(dest, url, rev_options, verbosity=verbosity)
 
     def unpack(self, location: str, url: HiddenText, verbosity: int) -> None:
         """


Test patch:
diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py
index dc38b2040..3726591ea 100644
--- a/tests/unit/test_vcs.py
+++ b/tests/unit/test_vcs.py
@@ -763,6 +763,410 @@ def test_subversion__get_remote_call_options(
     assert svn.get_remote_call_options() == expected_options
 
 
+class TestBazaarArgs(TestCase):
+    def setUp(self) -> None:
+        patcher = mock.patch("pip._internal.vcs.versioncontrol.call_subprocess")
+        self.addCleanup(patcher.stop)
+        self.call_subprocess_mock = patcher.start()
+
+        # Test Data.
+        self.url = "bzr+http://username:password@bzr.example.com/"
+        # use_interactive is set to False to test that remote call options are
+        # properly added.
+        self.svn = Bazaar()
+        self.rev_options = RevOptions(Bazaar)
+        self.dest = "/tmp/test"
+
+    def assert_call_args(self, args: CommandArgs) -> None:
+        assert self.call_subprocess_mock.call_args[0][0] == args
+
+    def test_fetch_new(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=1)
+        self.assert_call_args(
+            [
+                "bzr",
+                "checkout",
+                "--lightweight",
+                hide_url("bzr+http://username:password@bzr.example.com/"),
+                "/tmp/test",
+            ]
+        )
+
+    def test_fetch_new_quiet(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=0)
+        self.assert_call_args(
+            [
+                "bzr",
+                "checkout",
+                "--lightweight",
+                "--quiet",
+                hide_url("bzr+http://username:password@bzr.example.com/"),
+                "/tmp/test",
+            ]
+        )
+
+    def test_fetch_new_very_verbose(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=2)
+        self.assert_call_args(
+            [
+                "bzr",
+                "checkout",
+                "--lightweight",
+                "-vv",
+                hide_url("bzr+http://username:password@bzr.example.com/"),
+                "/tmp/test",
+            ]
+        )
+
+    def test_update(self) -> None:
+        self.svn.update(self.dest, hide_url(self.url), self.rev_options, verbosity=1)
+        self.assert_call_args(
+            [
+                "bzr",
+                "update",
+            ]
+        )
+
+    def test_update_quiet(self) -> None:
+        self.svn.update(self.dest, hide_url(self.url), self.rev_options, verbosity=0)
+        self.assert_call_args(
+            [
+                "bzr",
+                "update",
+                "-q",
+            ]
+        )
+
+
+class TestGitArgs(TestCase):
+    def setUp(self) -> None:
+        patcher = mock.patch("pip._internal.vcs.versioncontrol.call_subprocess")
+        self.addCleanup(patcher.stop)
+        self.call_subprocess_mock = patcher.start()
+
+        # Test Data.
+        self.url = "git+http://username:password@git.example.com/"
+        self.svn = Git()
+        self.rev_options = RevOptions(Git)
+        self.dest = "/tmp/test"
+
+    def test_fetch_new(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(2, 17)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.fetch_new(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=1
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "clone",
+            "--filter=blob:none",
+            hide_url("git+http://username:password@git.example.com/"),
+            "/tmp/test",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=1)
+
+    def test_fetch_new_legacy(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(1, 0)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.fetch_new(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=1
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "clone",
+            hide_url("git+http://username:password@git.example.com/"),
+            "/tmp/test",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=1)
+
+    def test_fetch_new_legacy_quiet(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(1, 0)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.fetch_new(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=0
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "clone",
+            "--quiet",
+            hide_url("git+http://username:password@git.example.com/"),
+            "/tmp/test",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=0)
+
+    def test_fetch_new_quiet(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(2, 17)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.fetch_new(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=0
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "clone",
+            "--filter=blob:none",
+            "--quiet",
+            hide_url("git+http://username:password@git.example.com/"),
+            "/tmp/test",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=0)
+
+    def test_switch(self) -> None:
+        with mock.patch.object(self.svn, "update_submodules") as update_submodules_mock:
+            self.svn.switch(
+                self.dest, hide_url(self.url), self.rev_options, verbosity=1
+            )
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "git",
+            "checkout",
+            "HEAD",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=1)
+
+    def test_switch_quiet(self) -> None:
+        with mock.patch.object(self.svn, "update_submodules") as update_submodules_mock:
+            self.svn.switch(
+                self.dest, hide_url(self.url), self.rev_options, verbosity=0
+            )
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "git",
+            "checkout",
+            "-q",
+            "HEAD",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=0)
+
+    def test_update(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(1, 9)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.update(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=1
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "fetch",
+            "--tags",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[2][0][0] == [
+            "git",
+            "reset",
+            "--hard",
+            "HEAD",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=1)
+
+    def test_update_legacy(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(1, 8)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.update(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=1
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "fetch",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[2][0][0] == [
+            "git",
+            "reset",
+            "--hard",
+            "HEAD",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=1)
+
+    def test_update_legacy_quiet(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(1, 9)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.update(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=0
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "fetch",
+            "--tags",
+            "-q",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[2][0][0] == [
+            "git",
+            "reset",
+            "--hard",
+            "-q",
+            "HEAD",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=0)
+
+    def test_update_quiet(self) -> None:
+        with mock.patch.object(self.svn, "get_git_version", return_value=(1, 8)):
+            with mock.patch.object(
+                self.svn, "update_submodules"
+            ) as update_submodules_mock:
+                self.svn.update(
+                    self.dest, hide_url(self.url), self.rev_options, verbosity=0
+                )
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "git",
+            "fetch",
+            "-q",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[2][0][0] == [
+            "git",
+            "reset",
+            "--hard",
+            "-q",
+            "HEAD",
+        ]
+
+        update_submodules_mock.assert_called_with(self.dest, verbosity=0)
+
+
+class TestMercurialArgs(TestCase):
+    def setUp(self) -> None:
+        patcher = mock.patch("pip._internal.vcs.versioncontrol.call_subprocess")
+        self.addCleanup(patcher.stop)
+        self.call_subprocess_mock = patcher.start()
+
+        # Test Data.
+        self.url = "hg+http://username:password@hg.example.com/"
+        self.svn = Mercurial()
+        self.rev_options = RevOptions(Mercurial)
+        self.dest = "/tmp/test"
+
+    def test_fetch_new(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=1)
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "hg",
+            "clone",
+            "--noupdate",
+            hide_url("hg+http://username:password@hg.example.com/"),
+            "/tmp/test",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "hg",
+            "update",
+        ]
+
+    def test_fetch_new_quiet(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=0)
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "hg",
+            "clone",
+            "--noupdate",
+            "--quiet",
+            hide_url("hg+http://username:password@hg.example.com/"),
+            "/tmp/test",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "hg",
+            "update",
+            "--quiet",
+        ]
+
+    def test_fetch_new_very_verbose(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=2)
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "hg",
+            "clone",
+            "--noupdate",
+            "--verbose",
+            hide_url("hg+http://username:password@hg.example.com/"),
+            "/tmp/test",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "hg",
+            "update",
+            "--verbose",
+        ]
+
+    def test_fetch_new_debug(self) -> None:
+        self.svn.fetch_new(self.dest, hide_url(self.url), self.rev_options, verbosity=3)
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "hg",
+            "clone",
+            "--noupdate",
+            "--verbose",
+            "--debug",
+            hide_url("hg+http://username:password@hg.example.com/"),
+            "/tmp/test",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "hg",
+            "update",
+            "--verbose",
+            "--debug",
+        ]
+
+    def test_update(self) -> None:
+        self.svn.update(self.dest, hide_url(self.url), self.rev_options, verbosity=1)
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "hg",
+            "pull",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "hg",
+            "update",
+        ]
+
+    def test_update_quiet(self) -> None:
+        self.svn.update(self.dest, hide_url(self.url), self.rev_options, verbosity=0)
+
+        assert self.call_subprocess_mock.call_args_list[0][0][0] == [
+            "hg",
+            "pull",
+            "-q",
+        ]
+
+        assert self.call_subprocess_mock.call_args_list[1][0][0] == [
+            "hg",
+            "update",
+            "-q",
+        ]
+
+
 class TestSubversionArgs(TestCase):
     def setUp(self) -> None:
         patcher = mock.patch("pip._internal.vcs.versioncontrol.call_subprocess")