repo: try to support opening uninitialized/broken repositories (#10736)

This `_get_remote_config()` should use `uninitialized=True` so that it supports
more broader kinds of broken/partially-initialized repositories, including some that have
`.dvc` directory missing, or `.git` directory missing.

This partially reverts #10719. #10608 is also fixed, and no longer requires `core.no_scm` to be passed.
This was already supported by `dvc.api.get_url` as it uses `uninitialized=True`, but this was not respected
in `_get_remote_config()` where it would fail before.

That said, this whole `open_repo`/`_get_remote_config` is terribly broken. For one, it is opening
a local repository, and forcing it's remote config to a repository opened with `Repo(rev=...)`,
where the config may be different.
This commit is contained in:
skshetry
2025-05-06 08:39:23 +00:00
committed by GitHub
parent f3be7d761f
commit 415d4bc980
2 changed files with 15 additions and 29 deletions
+10 -20
View File
@@ -1,4 +1,3 @@
import copy
import os
import tempfile
import threading
@@ -51,7 +50,7 @@ def open_repo(url, *args, **kwargs):
if os.path.exists(url):
url = os.path.abspath(url)
try:
config = _get_remote_config(url, *args, **kwargs)
config = _get_remote_config(url)
config.update(kwargs.get("config") or {})
kwargs["config"] = config
return Repo(url, *args, **kwargs)
@@ -98,24 +97,9 @@ def clean_repos():
_remove(path)
def _get_remote_config(url, *args, **kwargs):
def _get_remote_config(url):
try:
# Deepcopy to prevent modifying the original `kwargs['config']`
config = copy.deepcopy(kwargs.get("config"))
# Import operations will use this function to get the remote's cache. However,
# while the `url` sent will point to the external repo, the cache information
# in `kwargs["config"]["cache"]["dir"]`) will point to the local repo,
# see `dvc/dependency/repo.py:RepoDependency._make_fs()`
#
# This breaks this function, since we'd be instructing `Repo()` to use the wrong
# cache to being with. We need to remove the cache info from `kwargs["config"]`
# to read the actual remote repo data.
if config:
config.pop("cache", None)
repo = Repo(url, config=config)
repo = Repo(url, uninitialized=True)
except NotDvcRepoError:
return {}
@@ -125,9 +109,15 @@ def _get_remote_config(url, *args, **kwargs):
# Fill the empty upstream entry with a new remote pointing to the
# original repo's cache location.
name = "auto-generated-upstream"
try:
local_cache_dir = repo.cache.local_cache_dir
except AttributeError:
# if the `.dvc` dir is missing, we get an AttributeError
return {}
else:
return {
"core": {"remote": name},
"remote": {name: {"url": repo.cache.local_cache_dir}},
"remote": {name: {"url": local_cache_dir}},
}
# Use original remote to make sure that we are using correct url,
+2 -6
View File
@@ -5,7 +5,7 @@ from funcy import first, get_in
from dvc import api
from dvc.exceptions import OutputNotFoundError, PathMissingError
from dvc.scm import CloneError, SCMError
from dvc.scm import CloneError
from dvc.testing.api_tests import TestAPI # noqa: F401
from dvc.testing.tmp_dir import make_subrepo
from dvc.utils.fs import remove
@@ -79,11 +79,7 @@ def test_get_url_ignore_scm(tmp_dir, dvc, cloud, scm):
# Simulate gitless environment (e.g. deployed container)
(tmp_dir / ".git").rename(tmp_dir / "gitless_environment")
# Test failure mode when trying to access with git
with pytest.raises(SCMError, match="is not a git repository"):
api.get_url("foo", repo=repo_posix)
# Test successful access by ignoring git
assert api.get_url("foo", repo=repo_posix) == expected_url
assert (
api.get_url("foo", repo=repo_posix, config={"core": {"no_scm": True}})
== expected_url