Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect all registered VCS and choose inner-most #7593

Merged
merged 11 commits into from
Feb 8, 2020
19 changes: 11 additions & 8 deletions src/pip/_internal/vcs/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from pip._vendor.six.moves.urllib import parse as urllib_parse
from pip._vendor.six.moves.urllib import request as urllib_request

from pip._internal.exceptions import BadCommand
from pip._internal.exceptions import BadCommand, InstallationError
from pip._internal.utils.misc import display_path, hide_url
from pip._internal.utils.subprocess import make_command
from pip._internal.utils.temp_dir import TempDirectory
Expand Down Expand Up @@ -370,20 +370,23 @@ def update_submodules(cls, location):
)

@classmethod
def controls_location(cls, location):
if super(Git, cls).controls_location(location):
return True
def get_repository_root(cls, location):
loc = super(Git, cls).get_repository_root(location)
if loc:
return loc
try:
r = cls.run_command(['rev-parse'],
r = cls.run_command(['rev-parse', '--show-toplevel'],
cwd=location,
show_stdout=False,
on_returncode='ignore',
on_returncode='raise',
log_failed_cmd=False)
return not r
except BadCommand:
logger.debug("could not determine if %s is under git control "
"because git is not available", location)
return False
return None
except InstallationError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems weird, I'd say call_subprocess should not raise InstallationError which is too high-level.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is definitely not right, but I’d say this belongs to another (refactoring) PR. Note that the Mercurial implementation already catches InstallationError right now.

return None
return r


vcs.register(Git)
15 changes: 8 additions & 7 deletions src/pip/_internal/vcs/mercurial.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,20 @@ def get_subdirectory(cls, location):
return find_path_to_setup_from_repo_root(location, repo_root)

@classmethod
def controls_location(cls, location):
if super(Mercurial, cls).controls_location(location):
return True
def get_repository_root(cls, location):
loc = super(Mercurial, cls).get_repository_root(location)
if loc:
return loc
try:
cls.run_command(
['identify'],
r = cls.run_command(
['root'],
cwd=location,
show_stdout=False,
on_returncode='raise',
log_failed_cmd=False)
return True
except (BadCommand, InstallationError):
return False
return None
return r


vcs.register(Mercurial)
34 changes: 23 additions & 11 deletions src/pip/_internal/vcs/versioncontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,20 @@ def get_backend_for_dir(self, location):
Return a VersionControl object if a repository of that type is found
at the given directory.
"""
candidates = {}
for vcs_backend in self._registry.values():
if vcs_backend.controls_location(location):
logger.debug('Determine that %s uses VCS: %s',
location, vcs_backend.name)
return vcs_backend
return None
root = vcs_backend.get_repository_root(location)
if not root:
continue
logger.debug('Determine that %s uses VCS: %s',
location, vcs_backend.name)
candidates[root] = vcs_backend

# Choose the VCS in the inner-most directory (i.e. path to root
# is longest).
if not candidates:
return None
return candidates[max(candidates, key=len)]
uranusjr marked this conversation as resolved.
Show resolved Hide resolved

def get_backend_for_scheme(self, scheme):
# type: (str) -> Optional[VersionControl]
Expand Down Expand Up @@ -687,14 +695,18 @@ def is_repository_directory(cls, path):
return os.path.exists(os.path.join(path, cls.dirname))

@classmethod
def controls_location(cls, location):
# type: (str) -> bool
def get_repository_root(cls, location):
# type: (str) -> Optional[str]
"""
Check if a location is controlled by the vcs.
Return the "root" (top-level) directory controlled by the vcs,
or ``None`` if the directory is not in any.
uranusjr marked this conversation as resolved.
Show resolved Hide resolved

It is meant to be overridden to implement smarter detection
mechanisms for specific vcs.

This can do more than is_repository_directory() alone. For example,
the Git override checks that Git is actually available.
This can do more than is_repository_directory() alone. For
example, the Git override checks that Git is actually available.
"""
return cls.is_repository_directory(location)
if cls.is_repository_directory(location):
return location
return None