refuse to fetch from a remote that has no manifest

Otherwise, it can be confusing to clone from a wrong url, since it fails
to download a manifest and so appears as if the remote exists but is empty.

Sponsored-by: Jack Hill on Patreon
This commit is contained in:
Joey Hess 2024-05-13 09:47:21 -04:00
parent 424afe46d7
commit 3f848564ac
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
2 changed files with 31 additions and 21 deletions

View file

@ -111,7 +111,9 @@ capabilities = do
list :: State -> Remote -> Bool -> Annex State
list st rmt forpush = do
manifest <- downloadManifest rmt
manifest <- if forpush
then downloadManifestWhenPresent rmt
else downloadManifestOrFail rmt
l <- forM (inManifest manifest) $ \k -> do
b <- downloadGitBundle rmt k
heads <- inRepo $ Git.Bundle.listHeads b
@ -168,7 +170,7 @@ fetch st rmt [] = do
fetch' :: State -> Remote -> Annex ()
fetch' st rmt = do
manifest <- maybe (downloadManifest rmt) pure (manifestCache st)
manifest <- maybe (downloadManifestOrFail rmt) pure (manifestCache st)
forM_ (inManifest manifest) $ \k ->
downloadGitBundle rmt k >>= inRepo . Git.Bundle.unbundle
-- Newline indicates end of fetch.
@ -264,7 +266,8 @@ push st rmt ls = do
-- from it.
fullPush :: State -> Remote -> [Ref] -> Annex (Bool, State)
fullPush st rmt refs = guardPush st $ do
oldmanifest <- maybe (downloadManifest rmt) pure (manifestCache st)
oldmanifest <- maybe (downloadManifestWhenPresent rmt) pure
(manifestCache st)
let bs = map Git.Bundle.fullBundleSpec refs
bundlekey <- generateAndUploadGitBundle rmt bs oldmanifest
uploadManifest rmt (mkManifest [bundlekey] [])
@ -285,7 +288,7 @@ guardPush st a = catchNonAsync a $ \ex -> do
incrementalPush :: State -> Remote -> M.Map Ref Sha -> M.Map Ref Sha -> Annex (Bool, State)
incrementalPush st rmt oldtrackingrefs newtrackingrefs = guardPush st $ do
bs <- calc [] (M.toList newtrackingrefs)
oldmanifest <- maybe (downloadManifest rmt) pure (manifestCache st)
oldmanifest <- maybe (downloadManifestWhenPresent rmt) pure (manifestCache st)
bundlekey <- generateAndUploadGitBundle rmt bs oldmanifest
uploadManifest rmt (oldmanifest <> mkManifest [bundlekey] [])
return (True, st { manifestCache = Nothing })
@ -350,13 +353,14 @@ incrementalPush st rmt oldtrackingrefs newtrackingrefs = guardPush st $ do
)
-- When the push deletes all refs from the remote, upload an empty
-- manifest and then drop all bundles that were listed in it.
-- The manifest is emptired first so if this is interrupted, only
-- manifest and then drop all bundles that were listed in the manifest.
-- The manifest is emptied first so if this is interrupted, only
-- unused bundles will remain in the remote, rather than leaving the
-- remote with a manifest that refers to missing bundles.
pushEmpty :: State -> Remote -> Annex (Bool, State)
pushEmpty st rmt = do
manifest <- maybe (downloadManifest rmt) pure (manifestCache st)
manifest <- maybe (downloadManifestWhenPresent rmt) pure
(manifestCache st)
uploadManifest rmt mempty
ok <- allM (dropKey rmt)
(genManifestKey (Remote.uuid rmt) : inManifest manifest)
@ -534,17 +538,27 @@ checkSpecialRemoteProblems rmt
Just "Cannot use this thirdparty-populated special remote as a git remote"
| otherwise = Nothing
-- Downloads the Manifest, or if it does not exist, returns an empty
-- Manifest.
-- Downloads the Manifest when present in the remote. When not present,
-- returns an empty Manifest.
downloadManifestWhenPresent :: Remote -> Annex Manifest
downloadManifestWhenPresent rmt = fromMaybe mempty <$> downloadManifest rmt
-- Downloads the Manifest, or fails if the remote does not contain it.
downloadManifestOrFail :: Remote -> Annex Manifest
downloadManifestOrFail rmt =
maybe (giveup "No git repository found in this remote.") return
=<< downloadManifest rmt
-- Downloads the Manifest or Nothing if the remote does not contain a
-- manifest.
--
-- Throws errors if the remote cannot be accessed or the download fails,
-- or if the manifest file cannot be parsed.
--
-- This downloads the manifest to a temporary file, rather than using
-- the usual Annex.Transfer.download. The content of manifests is not
-- stable, and so it needs to re-download it fresh every time.
downloadManifest :: Remote -> Annex Manifest
downloadManifest :: Remote -> Annex (Maybe Manifest)
downloadManifest rmt = ifM (Remote.checkPresent rmt mk)
-- Downloads to a temporary file, rather than using
-- the usual Annex.Transfer.download. The content of manifests is
-- not stable, and so it needs to re-download it fresh every time.
( withTmpFile "GITMANIFEST" $ \tmp tmph -> do
liftIO $ hClose tmph
_ <- Remote.retrieveKeyFile rmt mk
@ -552,10 +566,11 @@ downloadManifest rmt = ifM (Remote.checkPresent rmt mk)
nullMeterUpdate Remote.NoVerify
(outks, inks) <- partitionEithers . map parseline . B8.lines
<$> liftIO (B.readFile tmp)
mkManifest
m <- mkManifest
<$> checkvalid [] inks
<*> checkvalid [] outks
, return mempty
return (Just m)
, return Nothing
)
where
mk = genManifestKey (Remote.uuid rmt)

View file

@ -28,11 +28,6 @@ This is implememented and working. Remaining todo list for it:
stored in the repo. Chicken and egg problem cloning from
such a remote. Maybe allow advanced users to force it?
* When the remote has no manifest, a pull from it should fail,
while a push should succeed. Otherwise, it can be confusing
to clone from a wrong url, since it fails to download
a manifest and so appears as if the remote is empty.
* Improve recovery from interrupted push by using outManifest to clean up
after it. (Requires populating outManifest.)