Skip to content

fix(import,update): stop writing .dvc metadata that disagrees with dvc (0.24.0) - #185

Merged
johnyaku merged 1 commit into
mainfrom
fix/import-update-metadata
Aug 20, 2026
Merged

johnyaku merged 1 commit into
mainfrom
fix/import-update-metadata

Conversation

@johnyaku

Copy link
Copy Markdown
Contributor

Fixes the four defects in #182 — the three in the report plus the size: 0 one in the comment.

What was wrong

outs.size: 0 on fresh multi-GB imports. get_file_size_from_cache built only the v3 object path. /g/data/a56/dvc/registries/visium-raw is mixed-layout, and the affected imports' leaf objects live only at the legacy v2 path <xx>/<rest> — so every lookup missed and if file_size: folded each miss into the total as zero. Checkout of the same objects worked throughout, because that path already falls back to v2, which is why the data was correct and only the size was wrong.

outs.size left describing the previous hash. dvc list --size against a repo URL reports no sizes — a .dir manifest holds only md5 and relpath, so the only place a size exists is the object itself — and a None size was read as "leave it alone", retaining a figure 3× the real directory.

deps.repo.rev left contradicting rev_lock, so the next plain dvc update resolved rev and rolled the import backwards. Also found: --rev <branch> was writing the branch name into rev_lock, which is meant to hold the resolved commit.

Rebuilt .dir omitting git-tracked files, then publishing that divergent manifest into a shared upstream remote. DVC's repo filesystem excludes only *.dvc, dvc.yaml, dvc.lock and .dvcignore, so importing a directory that is not itself an out hashes the git-tracked files there — including the .gitignore DVC generated — into the payload. dt works from hashes alone and cannot.

What changed

  • Object sizing goes through a shared cache_ops.object_size: both layouts, source remote then local cache. An object that cannot be found means no size field rather than a silent undercount, and a size belonging to a superseded hash is deleted rather than kept.
  • --rev <spec> records rev: <spec> and rev_lock: <resolved 40-char sha>. Without --rev, an import tracking a branch advances to that branch's tip, not to whatever the tmp clone has checked out. A pin contradicting the new lock is dropped, loudly.
  • dt import and dt update refuse a path that is not a single out, name the offending files, and push nothing. dt import previously died there with a bare AttributeError.
  • dt init seeds .dvcignore with .gitignore so a repo's own bookkeeping stops leaking into importers' data; dt doctor flags repos that lack it.

Two smaller bugs fixed in passing: the .dvc files inside a directory of outs were treated as payload candidates (DVC excludes them too), and a directory containing exactly one file was recorded as a single-file import, losing its .dir and nfiles.

Verification

Beyond unit tests (2352 passed, 1 skipped), each fix was driven end-to-end:

  • Against the real visium-raw registry, the 7 affected imports now size to 4.18–10.60 GB with nothing unsized, where the released code wrote 0.
  • Starting from the same stale state, dt update and a real dvc update now agree exactly: md5=0caa660ad774…dir size=101 nfiles=5 rev_lock=f8ad1237349b from both.
  • With .dvcignore seeded, dt import and dvc import of a non-out directory produce identical manifest hash, nfiles, size and entry list.
  • The refusal path leaves the .dvc file byte-identical and plants nothing in the source remote.
  • Checked against the real fastq paths (SciLife_FFPE_lymph_nodes/3946_1F_LN/fastq, its parent, SciLife_FFPE_batch6): all fully hashed, so the new guard does not disturb them.

The .gitignore behaviour was established by sandbox demo rather than inferred — DVC authors the file, importing the parent sweeps it in, hashing is raw md5 with no dos2unix, and upstream .dvcignore suppresses it with no side effects. Also worth knowing: dvc push cannot upload a repo import at all (stage_filter in dvc/repo/fetch.py excludes repo-import stages), so an imported directory is a permanent live dependency on the upstream repo.

🤖 Generated with Claude Code

…c (0.24.0)

Four defects from #182, all in the metadata dt records for repo imports.

outs.size: 0 on multi-GB imports. get_file_size_from_cache built only the
v3 object path, so a half-migrated remote -- v2 objects at <xx>/<rest>
alongside v3 ones -- reported no size for every v2 resident, and the caller
folded each miss into the total as zero. Checkout of the same objects
worked throughout, because that path already falls back to v2, which is why
the data was right and only the size was wrong. Sizing now goes through a
shared cache_ops.object_size (both layouts, source remote then local
cache); an object that cannot be found means no size field at all rather
than a silent undercount. Verified against visium-raw: the 7 affected
imports size to 4.18-10.60 GB, nothing unsized.

outs.size left describing the previous hash. `dvc list --size` against a
repo URL reports no sizes -- a .dir manifest holds only md5 and relpath, so
the only place a size exists is the object -- and a None size was read as
"leave it alone", retaining a figure 3x the real directory. Sizes are now
totalled by stat'ing the objects, and a size belonging to the old hash is
deleted rather than kept.

deps.repo.rev left contradicting rev_lock, so the next plain `dvc update`
resolved rev and rolled the import back. --rev now records the spec in rev
and the resolved 40-char sha in rev_lock (it was writing branch names into
rev_lock); without --rev, an import tracking a branch advances to that
branch's tip; a pin that contradicts the new lock is dropped, loudly.

Rebuilt .dir omitting git-tracked files. DVC's repo filesystem excludes
only *.dvc, dvc.yaml, dvc.lock and .dvcignore, so importing a directory
that is not itself an out hashes the git-tracked files there -- including
the .gitignore DVC generated -- into the payload. dt cannot, so its
manifest diverged, and was then published into a shared upstream remote.
dt import and dt update now refuse such a path and name the files, pushing
nothing. dt init seeds .dvcignore with .gitignore so a repo's own
bookkeeping stops leaking into importers' data, and dt doctor flags repos
that lack it. With that pattern in place dt import now matches dvc import
byte-for-byte on such a directory.

Two smaller bugs found in passing: the .dvc files inside a directory of
outs were treated as payload candidates (DVC excludes them too), and a
directory containing exactly one file was recorded as a single-file
import, losing its .dir and nfiles.

Co-Authored-By: Claude <noreply@anthropic.com>
@johnyaku
johnyaku merged commit 71537b3 into main Aug 20, 2026
1 check passed
@johnyaku
johnyaku deleted the fix/import-update-metadata branch August 20, 2026 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant