{ config, lib, stdenvNoCC, git, git-lfs, cacert, }: let urlToName = { url, rev, append, }: let shortRev = lib.sources.shortRev rev; appendShort = lib.optionalString ((builtins.match "[a-f0-9]*" rev) != null) "-${shortRev}"; in "${lib.sources.urlToName url}${if append == "" then appendShort else append}"; getRevWithTag = { rev ? null, tag ? null, }: if tag != null && rev != null then throw "fetchgit requires one of either `rev` or `tag` to be provided (not both)." else if tag != null then "refs/tags/${tag}" else if rev != null then rev else # FIXME fetching HEAD if no rev or tag is provided is problematic at best "HEAD"; in lib.makeOverridable ( lib.extendMkDerivation { constructDrv = stdenvNoCC.mkDerivation; excludeDrvArgNames = [ # Additional stdenv.mkDerivation arguments from derived fetchers. "derivationArgs" # Hashes, handled by `lib.fetchers.withNormalizedHash` # whose outputs contain outputHash* attributes. # Use `hash` when overriding with `.overrideAttrs`. "sha256" ]; extendDrvArgs = finalAttrs: lib.fetchers.withNormalizedHash { } ( # NOTE Please document parameter additions or changes in # ../../../doc/build-helpers/fetchers.chapter.md { url, tag ? null, rev ? null, name ? urlToName { inherit url; rev = lib.revOrTag finalAttrs.revCustom finalAttrs.tag; # when rootDir is specified, avoid invalidating the result when rev changes append = if rootDir != "" then "-${lib.strings.sanitizeDerivationName rootDir}" else ""; }, # When null, will default to: `deepClone || fetchTags` leaveDotGit ? null, outputHash ? lib.fakeHash, outputHashAlgo ? null, fetchSubmodules ? true, deepClone ? false, branchName ? null, # When null, will default to: `lib.optional (rootdir != "") rootdir` sparseCheckout ? null, # When null, will default to: `rootDir != ""` nonConeMode ? null, nativeBuildInputs ? [ ], # Shell code executed before the file has been fetched. This, in # particular, can do things like set NIX_PREFETCH_GIT_CHECKOUT_HOOK to # run operations between the checkout completing and deleting the .git # directory. preFetch ? "", # Shell code executed after `git checkout` and before .git directory removal/sanitization. postCheckout ? "", # Shell code executed after the file has been fetched # successfully. This can do things like check or transform the file. postFetch ? "", preferLocalBuild ? true, fetchLFS ? false, # Shell code to build a netrc file for BASIC auth netrcPhase ? null, # Impure env vars (https://nixos.org/nix/manual/#sec-advanced-attributes) # needed for netrcPhase netrcImpureEnvVars ? [ ], passthru ? { }, meta ? { }, allowedRequisites ? null, # fetch all tags after tree (useful for git describe) fetchTags ? false, # make this subdirectory the root of the result rootDir ? "", # GIT_CONFIG_GLOBAL (as a file) gitConfigFile ? config.gitConfigFile, # Additional stdenvNoCC.mkDerivation arguments. # It is typically for derived fetchers to pass down additional arguments, # and the specified arguments have lower precedence than other mkDerivation arguments. derivationArgs ? { }, }: /* NOTE: fetchgit has one problem: git fetch only works for refs. This is because fetching arbitrary (maybe dangling) commits creates garbage collection risks and checking whether a commit belongs to a ref is expensive. This may change in the future when some caching is added to git (?) Usually refs are either tags (refs/tags/*) or branches (refs/heads/*) Cloning branches will make the hash check fail when there is an update. But not all patches we want can be accessed by tags. The workaround is getting the last n commits so that it's likely that they still contain the hash we want. for now : increase depth iteratively (TODO) real fix: ask git folks to add a git fetch $HASH contained in $BRANCH facility because checking that $HASH is contained in $BRANCH is less expensive than fetching --depth $N. Even if git folks implemented this feature soon it may take years until server admins start using the new version? */ let finalHashHasColon = lib.hasInfix ":" finalAttrs.hash; finalHashColonMatch = lib.match "([^:]+)[:](.*)" finalAttrs.hash; in derivationArgs // { __structuredAttrs = true; inherit name; builder = ./builder.sh; fetcher = ./nix-prefetch-git; nativeBuildInputs = [ git cacert ] ++ lib.optionals fetchLFS [ git-lfs ] ++ nativeBuildInputs; hash = if outputHashAlgo == null || outputHash == "" || lib.hasPrefix outputHashAlgo outputHash then outputHash else "${outputHashAlgo}:${outputHash}"; outputHash = if finalAttrs.hash == "" then lib.fakeHash else if finalHashHasColon then lib.elemAt finalHashColonMatch 1 else finalAttrs.hash; outputHashAlgo = if finalHashHasColon then lib.head finalHashColonMatch else null; outputHashMode = "recursive"; sparseCheckout = let default = lib.optional (finalAttrs.rootDir != "") finalAttrs.rootDir; in lib.defaultTo default sparseCheckout; sparseCheckoutText = # Changed to throw on 2023-06-04 assert ( lib.assertMsg (lib.isList finalAttrs.sparseCheckout) "Please provide directories/patterns for sparse checkout as a list of strings. Passing a (multi-line) string is not supported any more." ); assert finalAttrs.nonConeMode -> (finalAttrs.sparseCheckout != [ ]); # git-sparse-checkout(1) says: # > When the --stdin option is provided, the directories or patterns are read # > from standard in as a newline-delimited list instead of from the arguments. builtins.concatStringsSep "\n" finalAttrs.sparseCheckout; inherit url fetchLFS fetchSubmodules deepClone branchName preFetch postCheckout postFetch fetchTags rootDir gitConfigFile ; leaveDotGit = if leaveDotGit != null then assert fetchTags -> leaveDotGit; assert rootDir != "" -> !leaveDotGit; leaveDotGit else deepClone || fetchTags; nonConeMode = lib.defaultTo (finalAttrs.rootDir != "") nonConeMode; inherit tag; revCustom = rev; rev = getRevWithTag { inherit (finalAttrs) tag; rev = finalAttrs.revCustom; }; postHook = if netrcPhase == null then null else '' ${netrcPhase} # required that git uses the netrc file mv {,.}netrc export NETRC=$PWD/.netrc export HOME=$PWD ''; impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ netrcImpureEnvVars ++ [ "GIT_PROXY_COMMAND" "NIX_GIT_SSL_CAINFO" "SOCKS_SERVER" # This is a parameter intended to be set by setup hooks or preFetch # scripts that want per-URL control over HTTP proxies used by Git # (if per-URL control isn't needed, `http_proxy` etc. will # suffice). It must be a whitespace-separated (with backslash as an # escape character) list of pairs like this: # # http://domain1/path1 proxy1 https://domain2/path2 proxy2 # # where the URLs are as documented in the `git-config` manual page # under `http..*`, and the proxies are as documented on the # same page under `http.proxy`. "FETCHGIT_HTTP_PROXIES" ]; outputChecks.out = { ${if allowedRequisites != null then "allowedRequisites" else null} = allowedRequisites; }; inherit preferLocalBuild meta; env = { NIX_PREFETCH_GIT_CHECKOUT_HOOK = finalAttrs.postCheckout; }; passthru = { gitRepoUrl = url; } // passthru; } ); # No ellipsis. inheritFunctionArgs = false; } ) // { inherit getRevWithTag; }