Skip to content

Commit

Permalink
Do not use shallow clones for short revisions
Browse files Browse the repository at this point in the history
When the `tag` of a `source-repository-stanza` is not a full hash,
we cannot do a shallow clone and fetch it separately.

Therefore, in those cases, we fall back to doing a full clone.

Fixes haskell#10605

Includes a regression test by @9999years.

Co-authored-by: Rebecca Turner <rbt@sent.as>
  • Loading branch information
9999years authored and alt-romes committed Dec 13, 2024
1 parent ae3f4d9 commit 9376e1a
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 46 deletions.
120 changes: 74 additions & 46 deletions cabal-install/src/Distribution/Client/VCS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -546,51 +546,56 @@ 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
Expand All @@ -608,8 +613,31 @@ 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]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# cabal v2-build
Configuration is affected by the following files:
- cabal.project
Resolving dependencies...
Build profile: -w ghc-<GHCVER> -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...
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Test.Cabal.Prelude

main = cabalTest $ do
cabal "v2-build" []
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Puppy () where

0 comments on commit 9376e1a

Please sign in to comment.