@@ -289,6 +289,55 @@ std::pair<ref<SourceAccessor>, FlakeRef> FlakeRef::lazyFetch(ref<Store> store) c
289289 return {accessor, FlakeRef (std::move (lockedInput), subdir)};
290290}
291291
292+ FlakeRef FlakeRef::canonicalize () const
293+ {
294+ auto flakeRef (*this );
295+
296+ /* Backward compatibility hack: In old versions of Nix, if you had
297+ a flake input like
298+
299+ inputs.foo.url = "git+https://foo/bar?dir=subdir";
300+
301+ it would result in a lock file entry like
302+
303+ "original": {
304+ "dir": "subdir",
305+ "type": "git",
306+ "url": "https://foo/bar?dir=subdir"
307+ }
308+
309+ New versions of Nix remove `?dir=subdir` from the `url` field,
310+ since the subdirectory is intended for `FlakeRef`, not the
311+ fetcher (and specifically the remote server), that is, the
312+ flakeref is parsed into
313+
314+ "original": {
315+ "dir": "subdir",
316+ "type": "git",
317+ "url": "https://foo/bar"
318+ }
319+
320+ However, this causes new versions of Nix to consider the lock
321+ file entry to be stale since the `original` ref no longer
322+ matches exactly.
323+
324+ For this reason, we canonicalise the `original` ref by
325+ filtering the `dir` query parameter from the URL. */
326+ if (auto url = fetchers::maybeGetStrAttr (flakeRef.input .attrs , " url" )) {
327+ try {
328+ auto parsed = parseURL (*url);
329+ if (auto dir2 = get (parsed.query , " dir" )) {
330+ if (flakeRef.subdir != " " && flakeRef.subdir == *dir2)
331+ parsed.query .erase (" dir" );
332+ }
333+ flakeRef.input .attrs .insert_or_assign (" url" , parsed.to_string ());
334+ } catch (BadURL &) {
335+ }
336+ }
337+
338+ return flakeRef;
339+ }
340+
292341std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec (
293342 const fetchers::Settings & fetchSettings,
294343 const std::string & url,
0 commit comments