Skip to content

Commit 9376e1a

Browse files
9999yearsalt-romes
authored andcommitted
Do not use shallow clones for short revisions
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 #10605 Includes a regression test by @9999years. Co-authored-by: Rebecca Turner <rbt@sent.as>
1 parent ae3f4d9 commit 9376e1a

File tree

6 files changed

+106
-46
lines changed

6 files changed

+106
-46
lines changed

cabal-install/src/Distribution/Client/VCS.hs

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -546,51 +546,56 @@ vcsGit =
546546
(\e -> if isPermissionError e then removePathForcibly dotGitModulesPath else throw e)
547547
else removeDirectoryRecursive dotGitModulesPath
548548

549-
-- If we want a particular branch or tag, fetch it.
550-
ref <- case srpBranch `mplus` srpTag of
551-
Nothing -> pure "HEAD"
552-
Just ref -> do
553-
-- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\
554-
-- /!\ MULTIPLE HOURS HAVE BEEN LOST HERE!! /!\
555-
-- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\
556-
--
557-
-- If you run `git fetch origin MY_TAG`, then the tag _will_ be
558-
-- fetched, but no local ref (e.g. `refs/tags/MY_TAG`) will be
559-
-- created.
560-
--
561-
-- This means that doing `git fetch origin MY_TAG && git reset --hard
562-
-- MY_TAG` will fail with a message like `unknown revision MY_TAG`.
563-
--
564-
-- There are two ways around this:
565-
--
566-
-- 1. Provide a refmap explicitly:
567-
--
568-
-- git fetch --refmap="+refs/tags/*:refs/tags/*" origin MYTAG
569-
--
570-
-- This tells Git to create local tags matching remote tags. It's
571-
-- not in the default refmap so you need to set it explicitly.
572-
-- (You can also set it with `git config set --local
573-
-- remote.origin.fetch ...`.)
574-
--
575-
-- 2. Use `FETCH_HEAD` directly: Git writes a `FETCH_HEAD` ref
576-
-- containing the commit that was just fetched. This feels a bit
577-
-- nasty but seems to work reliably, even if nothing was fetched.
578-
-- (That is, deleting `FETCH_HEAD` and re-running a `git fetch`
579-
-- command will succesfully recreate the `FETCH_HEAD` ref.)
580-
--
581-
-- Option 2 is what Cabal has done historically, and we're keeping it
582-
-- for now. Option 1 is possible but seems to have little benefit.
583-
git localDir ("fetch" : verboseArg ++ ["origin", ref])
584-
pure "FETCH_HEAD"
585-
586-
-- Then, reset to the appropriate ref.
587-
git localDir $
588-
"reset"
589-
: verboseArg
590-
++ [ "--hard"
591-
, ref
592-
, "--"
593-
]
549+
-- `doShallow` controls whether we use a shallow clone.
550+
-- If the clone is shallow, make sure to fetch specified revisions before
551+
-- using them.
552+
when doShallow $ do
553+
554+
-- If we want a particular branch or tag, fetch it.
555+
ref <- case srpBranch `mplus` srpTag of
556+
Nothing -> pure "HEAD"
557+
Just ref -> do
558+
-- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\
559+
-- /!\ MULTIPLE HOURS HAVE BEEN LOST HERE!! /!\
560+
-- /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\
561+
--
562+
-- If you run `git fetch origin MY_TAG`, then the tag _will_ be
563+
-- fetched, but no local ref (e.g. `refs/tags/MY_TAG`) will be
564+
-- created.
565+
--
566+
-- This means that doing `git fetch origin MY_TAG && git reset --hard
567+
-- MY_TAG` will fail with a message like `unknown revision MY_TAG`.
568+
--
569+
-- There are two ways around this:
570+
--
571+
-- 1. Provide a refmap explicitly:
572+
--
573+
-- git fetch --refmap="+refs/tags/*:refs/tags/*" origin MYTAG
574+
--
575+
-- This tells Git to create local tags matching remote tags. It's
576+
-- not in the default refmap so you need to set it explicitly.
577+
-- (You can also set it with `git config set --local
578+
-- remote.origin.fetch ...`.)
579+
--
580+
-- 2. Use `FETCH_HEAD` directly: Git writes a `FETCH_HEAD` ref
581+
-- containing the commit that was just fetched. This feels a bit
582+
-- nasty but seems to work reliably, even if nothing was fetched.
583+
-- (That is, deleting `FETCH_HEAD` and re-running a `git fetch`
584+
-- command will succesfully recreate the `FETCH_HEAD` ref.)
585+
--
586+
-- Option 2 is what Cabal has done historically, and we're keeping it
587+
-- for now. Option 1 is possible but seems to have little benefit.
588+
git localDir ("fetch" : verboseArg ++ ["origin", ref])
589+
pure "FETCH_HEAD"
590+
591+
-- Then, reset to the appropriate ref.
592+
git localDir $
593+
"reset"
594+
: verboseArg
595+
++ [ "--hard"
596+
, ref
597+
, "--"
598+
]
594599

595600
-- We need to check if `.gitmodules` exists _after_ the `git reset` call.
596601
gitModulesExists <- doesFileExist gitModulesPath
@@ -608,8 +613,31 @@ vcsGit =
608613
{ progInvokeCwd = Just cwd
609614
}
610615

616+
-- Beware: if the user supplied revision for the source repository
617+
-- package is /not/ a full hash, then we cannot fetch it, which means
618+
-- we cannot do a shallow clone (--depth=1).
619+
-- See #10605.
620+
doShallow
621+
| Nothing <- srpTag
622+
-- No tag, OK for shallow
623+
= True
624+
625+
-- full hashes are 40 characters
626+
| Just tg <- srpTag
627+
, length tg >= 40
628+
= True
629+
630+
| otherwise
631+
= False
632+
633+
depthIs1
634+
| doShallow = ["--depth=1"]
635+
| otherwise = []
636+
611637
cloneArgs =
612-
["clone", "--depth=1", "--no-checkout", loc, localDir]
638+
["clone" ]
639+
++ depthIs1
640+
++ [ "--no-checkout", loc, localDir]
613641
++ case peer of
614642
Nothing -> []
615643
Just peerLocalDir -> ["--reference", peerLocalDir]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# cabal v2-build
2+
Configuration is affected by the following files:
3+
- cabal.project
4+
Resolving dependencies...
5+
Build profile: -w ghc-<GHCVER> -O1
6+
In order, the following will be built:
7+
- puppy-1.0 (lib) (first run)
8+
Configuring library for puppy-1.0...
9+
Preprocessing library for puppy-1.0...
10+
Building library for puppy-1.0...
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
packages: .
2+
3+
-- Regression for https://github.com/haskell/cabal/issues/10605
4+
-- This is `my-lib` 0.9.
5+
source-repository-package
6+
type: git
7+
location: https://github.com/9999years/cabal-testsuite-my-lib.git
8+
tag: 9a0af0aa
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Test.Cabal.Prelude
2+
3+
main = cabalTest $ do
4+
cabal "v2-build" []
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cabal-version: 3.0
2+
name: puppy
3+
version: 1.0
4+
5+
library
6+
default-language: Haskell2010
7+
hs-source-dirs: src
8+
build-depends: base
9+
exposed-modules: Puppy
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module Puppy () where

0 commit comments

Comments
 (0)