Compare commits

...
15 Commits
Author SHA1 Message Date
Saugat Pachhai (सौगात) 7594d2f2ed optimize dvcignore 2026-09-07 21:58:33 +05:45
dependabot[bot] 56e5982951 build(deps): bump actions/setup-python from 6 to 7 (#11073)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-06 10:02:40 +05:45
dependabot[bot] defe4062aa build(deps): bump github/codeql-action from 4 to 4.37.4 (#11077)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4 to 4.37.4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4...v4.37.4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-06 10:02:19 +05:45
eeshsaxena f74c1c0e70 docs: fix parameter name mismatches in docstrings (#11060)
- repo/experiments/queue/base.py: `scm` -> `repo` in
  BaseStashQueue.__init__ (signature takes a Repo, not an SCM instance)
- repo/experiments/queue/tasks.py: stale `tmp_dir`/`entry_dict` args in
  cleanup_exp replaced with the actual `executor`/`infofile` parameters
2026-07-10 13:12:18 +05:45
dependabot[bot] b5dede14f2 build(deps): bump codecov/codecov-action from 6 to 7 (#11051)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6 to 7.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-04 18:16:33 +05:45
dependabot[bot] 8d37f966c3 build(deps): bump rtCamp/action-slack-notify from 2.3.3 to 2.4.0 (#11052)
Bumps [rtCamp/action-slack-notify](https://github.com/rtcamp/action-slack-notify) from 2.3.3 to 2.4.0.
- [Release notes](https://github.com/rtcamp/action-slack-notify/releases)
- [Commits](https://github.com/rtcamp/action-slack-notify/compare/v2.3.3...v2.4.0)

---
updated-dependencies:
- dependency-name: rtCamp/action-slack-notify
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-04 18:16:07 +05:45
pre-commit-ci[bot] 95165cc39a [pre-commit.ci] pre-commit autoupdate (#11053)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.15 → v0.15.20](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.15...v0.15.20)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-04 18:10:04 +05:45
dependabot[bot] ccb72126ea build(deps): bump actions/cache from 5 to 6 (#11056)
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-04 18:09:51 +05:45
dependabot[bot] d5315c3827 build(deps): bump actions/checkout from 6 to 7 (#11055)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-04 18:06:32 +05:45
pre-commit-ci[bot] 8131c32c33 [pre-commit.ci] pre-commit autoupdate (#11048)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.14 → v0.15.15](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.14...v0.15.15)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-02 14:07:40 +05:45
pre-commit-ci[bot] 0013af99d4 [pre-commit.ci] pre-commit autoupdate (#11040)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.12 → v0.15.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.12...v0.15.14)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-05-27 21:05:07 +05:45
pre-commit-ci[bot] 06ff81cd7f [pre-commit.ci] pre-commit autoupdate (#11031)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.11 → v0.15.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.11...v0.15.12)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-28 17:15:27 +05:45
pre-commit-ci[bot] e19b0874c0 [pre-commit.ci] pre-commit autoupdate (#11028)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.10 → v0.15.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.10...v0.15.11)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-26 09:26:45 +05:45
pre-commit-ci[bot] b290eaea43 [pre-commit.ci] pre-commit autoupdate (#11024)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.9 → v0.15.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.9...v0.15.10)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-14 15:24:00 +05:45
pre-commit-ci[bot] 2a2b543dd6 [pre-commit.ci] pre-commit autoupdate (#11023)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-07 12:10:50 +05:45
12 changed files with 202 additions and 59 deletions
+3 -3
View File
@@ -14,11 +14,11 @@ jobs:
name: run benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v6
- uses: actions/setup-python@v7
with:
python-version: "3.14"
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 0
@@ -32,7 +32,7 @@ jobs:
- name: run benchmarks on base branch
run: pytest --benchmark-autosave dvc/testing/benchmarks/ -k 'test_init or test_help'
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
clean: false
+2 -2
View File
@@ -19,11 +19,11 @@ jobs:
name: Build & Verify package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v6
- uses: actions/setup-python@v7
with:
python-version: "3.14"
+3 -3
View File
@@ -28,13 +28,13 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v4.37.4
with:
languages: ${{ matrix.language }}
queries: security-extended
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v4.37.4
+3 -3
View File
@@ -23,18 +23,18 @@ jobs:
plugin: ["dvc-s3"]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: dvc
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
repository: treeverse/${{ matrix.plugin }}
ref: main
path: ${{ matrix.plugin }}
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.pyv }}
+7 -7
View File
@@ -26,11 +26,11 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.14'
- uses: astral-sh/setup-uv@v7
@@ -42,7 +42,7 @@ jobs:
UV_LINK_MODE: ${{ runner.os == 'Windows' && 'symlink' || 'hardlink' }}
- name: Cache mypy
uses: actions/cache@v5
uses: actions/cache@v6
with:
path: .mypy_cache
key: mypy-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}
@@ -130,11 +130,11 @@ jobs:
pyv: "3.14"
pytestargs: "--splitting-algorithm=least_duration --splits 4 --group 4"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.pyv }}
- uses: astral-sh/setup-uv@v7
@@ -155,7 +155,7 @@ jobs:
pytest ${{ matrix.pytestargs }} -n=logical --dist=worksteal --timeout=300 --durations=0
--cov --cov-report=xml --cov-report=term --durations-path=./.github/.test_durations
- name: upload coverage report
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v7
check:
if: always()
needs: [tests]
@@ -170,7 +170,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2.3.3
uses: rtCamp/action-slack-notify@v2.4.0
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: ${{ job.status }}
+1 -1
View File
@@ -22,7 +22,7 @@ repos:
- id: sort-simple-yaml
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.15.8'
rev: 'v0.15.20'
hooks:
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
+48 -37
View File
@@ -60,13 +60,16 @@ class DvcIgnorePatterns(DvcIgnore):
self.pattern_list: list[PatternInfo] = []
self.dirname = dirname
self.find_matching_pattern = functools.cache(self._find_matching_pattern)
self._ignore_parents = functools.cache(self._ignore_parents)
regex_pattern_list: list[tuple[str, bool, bool, PatternInfo]] = []
for count, pattern_info in enumerate(pattern_infos):
for pattern_info in pattern_infos:
regex, ignore = GitIgnoreSpecPattern.pattern_to_regex(pattern_info.patterns)
if regex is not None and ignore is not None:
self.pattern_list.append(pattern_info)
regex = regex.replace(f"<{_DIR_MARK}>", f"<{_DIR_MARK}{count}>")
# DVC resolves directory matches itself; only the outer rule
# group is needed to identify a match in the combined regex.
regex = regex.replace(f"(?P<{_DIR_MARK}>", "(?:")
regex_pattern_list.append(
(regex, ignore, pattern_info.patterns.endswith("/"), pattern_info)
)
@@ -126,8 +129,20 @@ class DvcIgnorePatterns(DvcIgnore):
def __call__(
self, root: str, dirs: list[str], files: list[str]
) -> tuple[list[str], list[str]]:
files = [f for f in files if not self.matches(root, f)]
dirs = [d for d in dirs if not self.matches(root, d, True)]
# Entries are basenames from one directory listing, so normalize the
# shared prefix and check its ancestors only once.
prefix = self._get_normalize_path(root, "")
if prefix is None:
return dirs.copy(), files.copy()
if prefix and self._ignore_parents(prefix[:-1])[0]:
return [], []
files = [
f for f in files if not self.find_matching_pattern(f"{prefix}{f}", False)[0]
]
dirs = [
d for d in dirs if not self.find_matching_pattern(f"{prefix}{d}", True)[0]
]
return dirs, files
@@ -189,55 +204,51 @@ class DvcIgnorePatterns(DvcIgnore):
_match: list[PatternInfo] = []
if path:
result, _match = self._ignore(path, is_dir)
return (result, _match) if details else result
return (result, _match.copy()) if details else result
def _find_matching_pattern(
self, path: str, is_dir: bool
) -> tuple[bool, list[PatternInfo]]:
paths = [path]
if is_dir and not path.endswith("/"):
paths.append(f"{path}/")
for pattern, ignore, dir_only_pattern, pattern_map in reversed(
self.ignore_spec
):
if dir_only_pattern and not is_dir:
continue
for p in paths:
match = pattern.match(p)
if not match:
continue
if ignore:
group_name, _match = next(
(
(name, _match)
for name, _match in match.groupdict().items()
if name.startswith("rule_") and _match is not None
)
)
else:
# unignored patterns are not combined with `|`,
# so there are no groups.
group_name = None
_regex, pattern_info = pattern_map[group_name]
return ignore, [pattern_info]
match = pattern.match(path)
# Only directory-only patterns need the trailing slash. Probing it
# for other patterns would make X/** exclude X itself.
if not match and dir_only_pattern and not path.endswith("/"):
match = pattern.match(f"{path}/")
if not match:
continue
if ignore:
group_name = match.lastgroup
else:
# unignored patterns are not combined with `|`,
# so there are no groups.
group_name = None
_regex, pattern_info = pattern_map[group_name]
return ignore, [pattern_info]
return False, []
def _ignore(self, path: str, is_dir: bool) -> tuple[bool, list[PatternInfo]]:
parent, sep, _ = path.rpartition("/")
if sep:
result = self._ignore_parents(parent)
if result[0]:
return result
return self.find_matching_pattern(path, is_dir)
def _ignore_parents(self, path: str) -> tuple[bool, list[PatternInfo]]:
# Files in the same directory share the ancestor verdict, not just
# individual regex matches. Cache it to avoid rebuilding every prefix.
parts = path.split("/")
result = False
matches: list[PatternInfo] = []
for i in range(1, len(parts) + 1):
rel_path = "/".join(parts[:i])
result, _matches = self.find_matching_pattern(
rel_path, is_dir or i < len(parts)
)
if i < len(parts) and not result:
continue
matches.extend(_matches)
result, matches = self.find_matching_pattern(rel_path, True)
if result:
break
return result, matches
return result, matches
return False, []
def __hash__(self) -> int:
return hash(self.dirname + ":" + str(self.pattern_list))
+1 -1
View File
@@ -90,7 +90,7 @@ class BaseStashQueue(ABC):
"""Construct a queue.
Arguments:
scm: Git SCM instance for this queue.
repo: DVC repo instance for this queue.
ref: Git stash ref for this queue.
failed_ref: Failed run Git stash ref for this queue.
"""
+2 -2
View File
@@ -84,8 +84,8 @@ def cleanup_exp(executor: TempDirExecutor, infofile: str) -> None:
"""Cleanup after an experiment.
Arguments:
tmp_dir: Temp directory to be removed.
entry_dict: Serialized QueueEntry for this experiment.
executor: Executor to clean up.
infofile: Path to the executor info file.
"""
executor.cleanup(infofile)
+30
View File
@@ -19,6 +19,36 @@ def test_check_ignore(tmp_dir, dvc, file, ret, output, caplog, capsys):
assert "Having any troubles?" not in caplog.text
@pytest.mark.parametrize(
"pattern, exception, ignored",
[
("volumes/functions/**", "volumes/functions/deno.json", False),
("volumes/functions/**", "volumes/functions/deno.json*", False),
("volumes/functions/", "volumes/functions/deno.json", True),
("ignore.txt", "no-ignore.txt", False),
("*ignore.txt", "no-ignore.txt", False),
],
)
def test_check_ignore_reinclude(tmp_dir, dvc, pattern, exception, ignored):
tmp_dir.gen(
{
".dvcignore": f"{pattern}\n!{exception}\n",
"volumes": {"functions": {"deno.json": "", "other.py": ""}},
"ignore.txt": "",
"no-ignore.txt": "",
}
)
file = exception.rstrip("*").replace("/", os.sep)
sibling = (
os.path.join("volumes", "functions", "other.py")
if pattern.startswith("volumes/")
else "ignore.txt"
)
assert main(["check-ignore", "-q", file]) == (0 if ignored else 1)
assert main(["check-ignore", "-q", sibling]) == 0
@pytest.mark.parametrize(
"file,ret,output",
[
+44
View File
@@ -31,6 +31,50 @@ def test_ignore(tmp_dir, dvc, filename):
}
@pytest.mark.parametrize("suffix, ignored", [("/**", False), ("/", True), ("", True)])
@pytest.mark.parametrize("exception", ["deno.json", "deno.json*"])
def test_reinclude_directory_contents(tmp_dir, dvc, suffix, ignored, exception):
tmp_dir.gen(
{
".dvcignore": (
f"volumes/functions{suffix}\n!volumes/functions/{exception}\n"
),
"volumes": {"functions": {"deno.json": "", "other.py": ""}},
}
)
dvc._reset()
directory = tmp_dir / "volumes" / "functions"
assert dvc.dvcignore.is_ignored_dir(str(directory)) is ignored
assert dvc.dvcignore.is_ignored_file(str(directory / "deno.json")) is ignored
assert dvc.dvcignore.is_ignored_file(str(directory / "other.py"))
files = {
os.path.join(root, name)
for root, _, names in dvc.dvcignore.walk(dvc.fs, tmp_dir)
for name in names
}
expected = {str(tmp_dir / ".dvcignore")}
if not ignored:
expected.add(str(directory / "deno.json"))
assert files == expected
@pytest.mark.parametrize("pattern", ["ignore.txt", "*ignore.txt"])
def test_reinclude_root_file(tmp_dir, dvc, pattern):
tmp_dir.gen(
{
".dvcignore": f"{pattern}\n!no-ignore.txt\n",
"ignore.txt": "",
"no-ignore.txt": "",
}
)
dvc._reset()
assert dvc.dvcignore.is_ignored_file(str(tmp_dir / "ignore.txt"))
assert not dvc.dvcignore.is_ignored_file(str(tmp_dir / "no-ignore.txt"))
def test_walk(tmp_dir, dvc):
tmp_dir.gen(
{
+58
View File
@@ -4,6 +4,64 @@ from os.path import join
import pytest
from dvc.ignore import DvcIgnorePatterns
from dvc.pathspec_math import PatternInfo
def test_match_details_combined_patterns():
patterns = [
PatternInfo("*.tmp", ".dvcignore:1:*.tmp"),
PatternInfo("*.log", ".dvcignore:2:*.log"),
PatternInfo("build/", ".dvcignore:3:build/"),
PatternInfo("cache/", ".dvcignore:4:cache/"),
PatternInfo("!keep.log", ".dvcignore:5:!keep.log"),
]
ignore = DvcIgnorePatterns(patterns, "/repo", "/")
for _ in range(2):
assert ignore.matches("/repo", "data/error.log", details=True) == (
True,
[patterns[1]],
)
assert ignore.matches("/repo", "cache/keep.log", details=True) == (
True,
[patterns[3]],
)
assert ignore.matches("/repo", "data/keep.log", details=True) == (
False,
[patterns[4]],
)
@pytest.mark.parametrize("path", ["data/file.tmp", "build/file.txt"])
def test_match_details_mutation(path):
ignore = DvcIgnorePatterns(["*.tmp", "build/"], "/repo", "/")
result, details = ignore.matches("/repo", path, details=True)
expected = details.copy()
details.clear()
assert result
assert expected
assert ignore.matches("/repo", path, details=True) == (True, expected)
@pytest.mark.parametrize(
"root, expected_dirs, expected_files",
[
("/repo", ["child"], ["keep.txt", "logs"]),
("/repo/dir", [], ["keep.txt"]),
("/repo/ignored/dir", [], []),
("/repo2", ["logs", "ignored", "child"], ["keep.txt", "other.tmp", "logs"]),
],
)
def test_filter_directory_listing(root, expected_dirs, expected_files):
ignore = DvcIgnorePatterns(
["ignored/", "dir/**", "!dir/keep.txt", "logs/", "*.tmp"], "/repo", "/"
)
dirs = ["logs", "ignored", "child"]
files = ["keep.txt", "other.tmp", "logs"]
for _ in range(2):
assert ignore(root, dirs, files) == (expected_dirs, expected_files)
assert dirs == ["logs", "ignored", "child"]
assert files == ["keep.txt", "other.tmp", "logs"]
@pytest.mark.parametrize(