
When annex.freezecontent-command or annex.thawcontent-command is configured but fails, prevent initialization. This allows the user to fix their configuration and avoid crippled filesystem detection entering an adjusted unlocked branch unexpectedly, when they had been relying on the hooks working around their filesystems's infelicities. In the case of git-remote-annex, a failure of these hooks is taken to mean the filesystem may be crippled, so it deletes the bundles objects and avoids initialization. That might mean extra work, but only in this edge case where the hook is misconfigured. And it keeps the command working for cloning even despite the misconfiguration. Sponsored-by: the NIH-funded NICEMAN (ReproNim TR&D3) project
172 lines
5.5 KiB
Haskell
172 lines
5.5 KiB
Haskell
{- git-annex git hooks
|
|
-
|
|
- Note that it's important that the content of scripts installed by
|
|
- git-annex not change, otherwise removing old hooks using an old
|
|
- version of the script would fail.
|
|
-
|
|
- Copyright 2013-2025 Joey Hess <id@joeyh.name>
|
|
-
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Annex.Hook where
|
|
|
|
import Annex.Common
|
|
import qualified Git.Hook as Git
|
|
import qualified Annex
|
|
import Utility.Shell
|
|
|
|
import qualified Data.Map as M
|
|
|
|
preCommitHook :: Git.Hook
|
|
preCommitHook = Git.Hook (literalOsPath "pre-commit")
|
|
(mkHookScript "git annex pre-commit .") []
|
|
|
|
postReceiveHook :: Git.Hook
|
|
postReceiveHook = Git.Hook (literalOsPath "post-receive")
|
|
-- Only run git-annex post-receive when git-annex supports it,
|
|
-- to avoid failing if the repository with this hook is used
|
|
-- with an older version of git-annex.
|
|
(mkHookScript "if git annex post-receive --help >/dev/null 2>&1; then git annex post-receive; fi")
|
|
-- This is an old version of the hook script.
|
|
[ mkHookScript "git annex post-receive"
|
|
]
|
|
|
|
postCheckoutHook :: Git.Hook
|
|
postCheckoutHook = Git.Hook (literalOsPath "post-checkout") smudgeHook []
|
|
|
|
postMergeHook :: Git.Hook
|
|
postMergeHook = Git.Hook (literalOsPath "post-merge") smudgeHook []
|
|
|
|
-- Older versions of git-annex didn't support this command, but neither did
|
|
-- they support v7 repositories.
|
|
smudgeHook :: String
|
|
smudgeHook = mkHookScript "git annex smudge --update"
|
|
|
|
preCommitAnnexHook :: Git.Hook
|
|
preCommitAnnexHook = Git.Hook (literalOsPath "pre-commit-annex") "" []
|
|
|
|
postUpdateAnnexHook :: Git.Hook
|
|
postUpdateAnnexHook = Git.Hook (literalOsPath "post-update-annex") "" []
|
|
|
|
preInitAnnexHook :: Git.Hook
|
|
preInitAnnexHook = Git.Hook (literalOsPath "pre-init-annex") "" []
|
|
|
|
freezeContentAnnexHook :: Git.Hook
|
|
freezeContentAnnexHook = Git.Hook (literalOsPath "freezecontent-annex") "" []
|
|
|
|
thawContentAnnexHook :: Git.Hook
|
|
thawContentAnnexHook = Git.Hook (literalOsPath "thawcontent-annex") "" []
|
|
|
|
secureEraseAnnexHook :: Git.Hook
|
|
secureEraseAnnexHook = Git.Hook (literalOsPath "secure-erase-annex") "" []
|
|
|
|
commitMessageAnnexHook :: Git.Hook
|
|
commitMessageAnnexHook = Git.Hook (literalOsPath "commitmessage-annex") "" []
|
|
|
|
httpHeadersAnnexHook :: Git.Hook
|
|
httpHeadersAnnexHook = Git.Hook (literalOsPath "http-headers-annex") "" []
|
|
|
|
mkHookScript :: String -> String
|
|
mkHookScript s = unlines
|
|
[ shebang
|
|
, "# automatically configured by git-annex"
|
|
, s
|
|
]
|
|
|
|
hookWrite :: Git.Hook -> Annex ()
|
|
hookWrite h = unlessM (inRepo $ Git.hookWrite h) $
|
|
hookWarning h "already exists, not configuring"
|
|
|
|
hookUnWrite :: Git.Hook -> Annex ()
|
|
hookUnWrite h = unlessM (inRepo $ Git.hookUnWrite h) $
|
|
hookWarning h "contents modified; not deleting. Edit it to remove call to git annex."
|
|
|
|
hookWarning :: Git.Hook -> String -> Annex ()
|
|
hookWarning h msg = do
|
|
r <- gitRepo
|
|
warning $ UnquotedString $
|
|
fromOsPath (Git.hookName h) ++
|
|
" hook (" ++ fromOsPath (Git.hookFile h r) ++ ") " ++ msg
|
|
|
|
{- To avoid checking if the hook exists every time, the existing hooks
|
|
- are cached. -}
|
|
doesAnnexHookExist :: Git.Hook -> Annex Bool
|
|
doesAnnexHookExist hook = do
|
|
m <- Annex.getState Annex.existinghooks
|
|
case M.lookup hook m of
|
|
Just exists -> return exists
|
|
Nothing -> do
|
|
exists <- inRepo $ Git.hookExists hook
|
|
Annex.changeState $ \s -> s
|
|
{ Annex.existinghooks = M.insert hook exists m }
|
|
return exists
|
|
|
|
runAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex ()
|
|
runAnnexHook hook commandcfg = runAnnexHook' hook commandcfg >>= \case
|
|
HookSuccess -> noop
|
|
HookFailed failedcommanddesc ->
|
|
warning $ UnquotedString $ failedcommanddesc ++ " failed"
|
|
|
|
data HookResult
|
|
= HookSuccess
|
|
| HookFailed String
|
|
-- ^ A description of the hook command that failed.
|
|
deriving (Eq, Show)
|
|
|
|
runAnnexHook' :: Git.Hook -> (GitConfig -> Maybe String) -> Annex HookResult
|
|
runAnnexHook' hook commandcfg = ifM (doesAnnexHookExist hook)
|
|
( runhook
|
|
, runcommandcfg
|
|
)
|
|
where
|
|
runhook = ifM (inRepo $ Git.runHook boolSystem hook [])
|
|
( return HookSuccess
|
|
, do
|
|
h <- fromRepo (Git.hookFile hook)
|
|
return $ HookFailed $ fromOsPath h
|
|
)
|
|
runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
|
|
Nothing -> return HookSuccess
|
|
Just command ->
|
|
ifM (liftIO $ boolSystem "sh" [Param "-c", Param command])
|
|
( return HookSuccess
|
|
, return $ HookFailed $ "git configured command '" ++ command ++ "'"
|
|
)
|
|
|
|
runAnnexPathHook :: String -> Git.Hook -> (GitConfig -> Maybe String) -> OsPath -> Annex HookResult
|
|
runAnnexPathHook pathtoken hook commandcfg p = ifM (doesAnnexHookExist hook)
|
|
( runhook
|
|
, runcommandcfg
|
|
)
|
|
where
|
|
runhook = ifM (inRepo $ Git.runHook boolSystem hook [ File p' ])
|
|
( return HookSuccess
|
|
, do
|
|
h <- fromRepo (Git.hookFile hook)
|
|
return $ HookFailed $ fromOsPath h
|
|
)
|
|
runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
|
|
Nothing -> return HookSuccess
|
|
Just basecmd ->
|
|
ifM (liftIO $ boolSystem "sh" [Param "-c", Param (gencmd basecmd)])
|
|
( return HookSuccess
|
|
, return $ HookFailed $ "git configured command '" ++ basecmd ++ "'"
|
|
)
|
|
gencmd = massReplace [ (pathtoken, shellEscape p') ]
|
|
p' = fromOsPath p
|
|
|
|
outputOfAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex (Maybe String)
|
|
outputOfAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook)
|
|
( runhook
|
|
, runcommandcfg
|
|
)
|
|
where
|
|
runhook = inRepo (Git.runHook runhook' hook [])
|
|
runhook' c ps = Just <$> readProcess c (toCommand ps)
|
|
runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
|
|
Nothing -> return Nothing
|
|
Just command -> liftIO $
|
|
Just <$> readProcess "sh" ["-c", command]
|