diff --git a/cabal-install/src/Distribution/Client/VCS.hs b/cabal-install/src/Distribution/Client/VCS.hs index 98b8251d9c1..0245af6f82a 100644 --- a/cabal-install/src/Distribution/Client/VCS.hs +++ b/cabal-install/src/Distribution/Client/VCS.hs @@ -546,51 +546,55 @@ vcsGit = (\e -> if isPermissionError e then removePathForcibly dotGitModulesPath else throw e) else removeDirectoryRecursive dotGitModulesPath - -- If we want a particular branch or tag, fetch it. - ref <- case srpBranch `mplus` srpTag of - Nothing -> pure "HEAD" - Just ref -> do - -- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ - -- /!\ MULTIPLE HOURS HAVE BEEN LOST HERE!! /!\ - -- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ - -- - -- If you run `git fetch origin MY_TAG`, then the tag _will_ be - -- fetched, but no local ref (e.g. `refs/tags/MY_TAG`) will be - -- created. - -- - -- This means that doing `git fetch origin MY_TAG && git reset --hard - -- MY_TAG` will fail with a message like `unknown revision MY_TAG`. - -- - -- There are two ways around this: - -- - -- 1. Provide a refmap explicitly: - -- - -- git fetch --refmap="+refs/tags/*:refs/tags/*" origin MYTAG - -- - -- This tells Git to create local tags matching remote tags. It's - -- not in the default refmap so you need to set it explicitly. - -- (You can also set it with `git config set --local - -- remote.origin.fetch ...`.) - -- - -- 2. Use `FETCH_HEAD` directly: Git writes a `FETCH_HEAD` ref - -- containing the commit that was just fetched. This feels a bit - -- nasty but seems to work reliably, even if nothing was fetched. - -- (That is, deleting `FETCH_HEAD` and re-running a `git fetch` - -- command will succesfully recreate the `FETCH_HEAD` ref.) - -- - -- Option 2 is what Cabal has done historically, and we're keeping it - -- for now. Option 1 is possible but seems to have little benefit. - git localDir ("fetch" : verboseArg ++ ["origin", ref]) - pure "FETCH_HEAD" - - -- Then, reset to the appropriate ref. - git localDir $ - "reset" - : verboseArg - ++ [ "--hard" - , ref - , "--" - ] + -- `doShallow` controls whether we use a shallow clone. + -- If the clone is shallow, make sure to fetch specified revisions before + -- using them. + when doShallow $ do + -- If we want a particular branch or tag, fetch it. + ref <- case srpBranch `mplus` srpTag of + Nothing -> pure "HEAD" + Just ref -> do + -- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ + -- /!\ MULTIPLE HOURS HAVE BEEN LOST HERE!! /!\ + -- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ + -- + -- If you run `git fetch origin MY_TAG`, then the tag _will_ be + -- fetched, but no local ref (e.g. `refs/tags/MY_TAG`) will be + -- created. + -- + -- This means that doing `git fetch origin MY_TAG && git reset --hard + -- MY_TAG` will fail with a message like `unknown revision MY_TAG`. + -- + -- There are two ways around this: + -- + -- 1. Provide a refmap explicitly: + -- + -- git fetch --refmap="+refs/tags/*:refs/tags/*" origin MYTAG + -- + -- This tells Git to create local tags matching remote tags. It's + -- not in the default refmap so you need to set it explicitly. + -- (You can also set it with `git config set --local + -- remote.origin.fetch ...`.) + -- + -- 2. Use `FETCH_HEAD` directly: Git writes a `FETCH_HEAD` ref + -- containing the commit that was just fetched. This feels a bit + -- nasty but seems to work reliably, even if nothing was fetched. + -- (That is, deleting `FETCH_HEAD` and re-running a `git fetch` + -- command will succesfully recreate the `FETCH_HEAD` ref.) + -- + -- Option 2 is what Cabal has done historically, and we're keeping it + -- for now. Option 1 is possible but seems to have little benefit. + git localDir ("fetch" : verboseArg ++ ["origin", ref]) + pure "FETCH_HEAD" + + -- Then, reset to the appropriate ref. + git localDir $ + "reset" + : verboseArg + ++ [ "--hard" + , ref + , "--" + ] -- We need to check if `.gitmodules` exists _after_ the `git reset` call. gitModulesExists <- doesFileExist gitModulesPath @@ -608,8 +612,29 @@ vcsGit = { progInvokeCwd = Just cwd } + -- Beware: if the user supplied revision for the source repository + -- package is /not/ a full hash, then we cannot fetch it, which means + -- we cannot do a shallow clone (--depth=1). + -- See #10605. + doShallow + | Nothing <- srpTag = + -- No tag, OK for shallow + True + -- full hashes are 40 characters + | Just tg <- srpTag + , length tg >= 40 = + True + | otherwise = + False + + depthIs1 + | doShallow = ["--depth=1"] + | otherwise = [] + cloneArgs = - ["clone", "--depth=1", "--no-checkout", loc, localDir] + ["clone"] + ++ depthIs1 + ++ [ "--no-checkout", loc, localDir] ++ case peer of Nothing -> [] Just peerLocalDir -> ["--reference", peerLocalDir] @@ -854,8 +879,8 @@ vcsPijul = [programInvocation prog cloneArgs] -- And if there's a tag, we have to do that in a second step: ++ [ (programInvocation prog (checkoutArgs tag)) - { progInvokeCwd = Just destdir - } + { progInvokeCwd = Just destdir + } | tag <- maybeToList (srpTag repo) ] where diff --git a/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.out b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.out new file mode 100644 index 00000000000..b5579a8defc --- /dev/null +++ b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.out @@ -0,0 +1,10 @@ +# cabal v2-build +Configuration is affected by the following files: +- cabal.project +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - puppy-1.0 (lib) (first run) +Configuring library for puppy-1.0... +Preprocessing library for puppy-1.0... +Building library for puppy-1.0... diff --git a/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.project b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.project new file mode 100644 index 00000000000..275c4fc4bb6 --- /dev/null +++ b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.project @@ -0,0 +1,8 @@ +packages: . + +-- Regression for https://github.com/haskell/cabal/issues/10605 +-- This is `my-lib` 0.9. +source-repository-package + type: git + location: https://github.com/9999years/cabal-testsuite-my-lib.git + tag: 9a0af0aa diff --git a/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.test.hs b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.test.hs new file mode 100644 index 00000000000..d8024a44c0a --- /dev/null +++ b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/cabal.test.hs @@ -0,0 +1,4 @@ +import Test.Cabal.Prelude + +main = cabalTest $ do + cabal "v2-build" [] diff --git a/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/puppy.cabal b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/puppy.cabal new file mode 100644 index 00000000000..d4dbe921e99 --- /dev/null +++ b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/puppy.cabal @@ -0,0 +1,9 @@ +cabal-version: 3.0 +name: puppy +version: 1.0 + +library + default-language: Haskell2010 + hs-source-dirs: src + build-depends: base + exposed-modules: Puppy diff --git a/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/src/Puppy.hs b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/src/Puppy.hs new file mode 100644 index 00000000000..47209451314 --- /dev/null +++ b/cabal-testsuite/PackageTests/SourceRepositoryPackageShallow/src/Puppy.hs @@ -0,0 +1 @@ +module Puppy () where