mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: resolve a bare cited name at the pin first
A ref citing a bare filename, the way prose does, was matched against the HEAD tree alone. A file that moved since the pin then resolved to its HEAD path, which does not exist at the pin, and the ref reported GONE with "pin revision missing": it failed for the one reason it never should, its own success at HEAD. The pin tree is searched first, HEAD stays the fallback, and the rename search carries the pin path forward as it does for any written path. This is what the resolver already documents for prefixed and suffixed paths; the bare-name branch was the one that did not follow it. linapple cites Memory.cpp, src/Memory.cpp at its pin and src/apple2/Memory.cpp today. Its refs now name where the code went instead of reporting it missing.
This commit is contained in:
1 parent
11da070128
commit
6a0fb1e776
2 files changed
+70
-13
No files matched your search
+29
-13
@@ -1230,6 +1230,7 @@ def build_report(
|
||||
|
||||
owners: dict[tuple[str, int | None], tuple[RepoView, str]] = {}
|
||||
context: dict[str, object] = {}
|
||||
pin_trees: dict[str, list[str] | None] = {}
|
||||
|
||||
def _locate(
|
||||
path: str, start: int | None = None, tokens: tuple = ()
|
||||
@@ -1251,20 +1252,27 @@ def build_report(
|
||||
if tail and any(head == v.repo.name for v in views):
|
||||
candidates.append(tail)
|
||||
if "/" not in path:
|
||||
# A bare filename, the way prose cites files. The HEAD tree of
|
||||
# each repository resolves it when exactly one path carries it.
|
||||
# A bare filename, the way prose cites files. Resolved against the
|
||||
# pin before HEAD: the ref was written at the pin, and a file that
|
||||
# moved since would otherwise be resolved to a HEAD path that does
|
||||
# not exist at the pin, reporting GONE for the one reason it never
|
||||
# should. The rename search then carries the pin path to HEAD.
|
||||
for view in views:
|
||||
_, tree = _context_for(view)
|
||||
matches = [
|
||||
p for p in tree or []
|
||||
if posixpath.basename(p) == path
|
||||
]
|
||||
if len(matches) > 1:
|
||||
matches = narrow_by_cited(matches, cited_dirs)
|
||||
# A few survivors are told apart by the cited line below: a
|
||||
# 266-line Windows configure.ac cannot carry line 754.
|
||||
if len(matches) <= 4:
|
||||
candidates.extend(m for m in matches if m not in candidates)
|
||||
for tree in (_pin_tree_for(view), _context_for(view)[1]):
|
||||
matches = [
|
||||
p for p in tree or []
|
||||
if posixpath.basename(p) == path
|
||||
]
|
||||
if len(matches) > 1:
|
||||
matches = narrow_by_cited(matches, cited_dirs)
|
||||
# A few survivors are told apart by the cited line below: a
|
||||
# 266-line Windows configure.ac cannot carry line 754.
|
||||
if len(matches) <= 4:
|
||||
candidates.extend(
|
||||
m for m in matches if m not in candidates
|
||||
)
|
||||
if matches:
|
||||
break
|
||||
elif not any(
|
||||
upstream.fetch_file(v.repo, sha, path, cache_dir, offline) is not None
|
||||
for v in views for sha in (v.pin, v.head)
|
||||
@@ -1339,6 +1347,14 @@ def build_report(
|
||||
owners[key] = _locate(path, start, tokens)
|
||||
return owners[key]
|
||||
|
||||
def _pin_tree_for(view: RepoView):
|
||||
"""Tree at the revision the refs were written against, memoised."""
|
||||
key = view.repo.slug
|
||||
if key not in pin_trees:
|
||||
tree, _ = upstream.list_tree(view.repo, view.pin, cache_dir, offline)
|
||||
pin_trees[key] = tree
|
||||
return pin_trees[key]
|
||||
|
||||
def _context_for(view: RepoView):
|
||||
key = view.repo.slug
|
||||
if key not in context:
|
||||
|
||||
@@ -1367,6 +1367,47 @@ def _sample_report():
|
||||
)
|
||||
|
||||
|
||||
class TestBareNameResolvedAtThePin(TestBuildReport):
|
||||
"""A bare filename belongs to the revision the ref was written against.
|
||||
|
||||
Resolution searched the HEAD tree only, so a file that moved between the
|
||||
pin and HEAD was resolved to its HEAD path and then read at the pin,
|
||||
where that path does not exist yet: the ref reported GONE for the one
|
||||
reason it should never report, its own success at HEAD. linapple cites
|
||||
Memory.cpp, src/Memory.cpp at its pin and src/apple2/Memory.cpp today.
|
||||
"""
|
||||
|
||||
def _tree_per_revision(self):
|
||||
def list_tree(repo, sha, cache_dir, offline=False):
|
||||
return sorted({path for rev, path in self.files if rev == sha}), False
|
||||
|
||||
profile_sync.upstream.list_tree = list_tree
|
||||
|
||||
def test_a_bare_name_follows_the_file_that_moved_after_the_pin(self):
|
||||
self._tree_per_revision()
|
||||
self.files[("pinsha", "src/Memory.cpp")] = ["x", "the cited line", "y"]
|
||||
self.files[("headsha", "src/apple2/Memory.cpp")] = [
|
||||
"pad", "x", "the cited line", "y",
|
||||
]
|
||||
report = build_report("test", self._profile(["Memory.cpp:2"]), self.dir)
|
||||
part = report.entries[0].parts[0]
|
||||
self.assertNotEqual(
|
||||
part.status, "GONE",
|
||||
"the bare name resolved at HEAD and was then read at the pin",
|
||||
)
|
||||
self.assertEqual(part.new_path, "src/apple2/Memory.cpp")
|
||||
|
||||
def test_a_bare_name_present_at_both_revisions_resolves_once(self):
|
||||
"""A resolved bare name reads RENAMED so the recale writes the path."""
|
||||
self._tree_per_revision()
|
||||
self.files[("pinsha", "src/New.cpp")] = ["x", "the cited line"]
|
||||
self.files[("headsha", "src/New.cpp")] = ["x", "the cited line"]
|
||||
report = build_report("test", self._profile(["New.cpp:2"]), self.dir)
|
||||
part = report.entries[0].parts[0]
|
||||
self.assertEqual(part.status, "RENAMED")
|
||||
self.assertEqual(part.new_path, "src/New.cpp")
|
||||
|
||||
|
||||
class TestFormatReport(unittest.TestCase):
|
||||
def test_header_shows_both_revisions(self):
|
||||
text = format_report(_sample_report())
|
||||
|
||||
Reference in new issue
Block a user