2016-04-06 19:33:29 +00:00
|
|
|
{- Temporarily changing the files git uses.
|
|
|
|
-
|
2020-04-10 17:37:04 +00:00
|
|
|
- Copyright 2014-2020 Joey Hess <id@joeyh.name>
|
2016-04-06 19:33:29 +00:00
|
|
|
-
|
2019-03-13 19:48:14 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2016-04-06 19:33:29 +00:00
|
|
|
-}
|
|
|
|
|
2020-04-10 17:37:04 +00:00
|
|
|
module Annex.GitOverlay (
|
|
|
|
module Annex.GitOverlay,
|
|
|
|
AltIndexFile(..),
|
|
|
|
) where
|
2016-04-06 19:33:29 +00:00
|
|
|
|
|
|
|
import qualified Control.Exception as E
|
|
|
|
|
|
|
|
import Annex.Common
|
2020-04-10 17:37:04 +00:00
|
|
|
import Types.IndexFiles
|
2016-04-06 19:33:29 +00:00
|
|
|
import Git
|
|
|
|
import Git.Types
|
2016-05-17 17:30:59 +00:00
|
|
|
import Git.Index
|
2016-04-06 19:33:29 +00:00
|
|
|
import Git.Env
|
|
|
|
import qualified Annex
|
withAltRepo needs a separate queue of changes
The queue could potentially contain changes from before withAltRepo, and
get flushed inside the call, which would apply the changes to the modified
repo.
Or, changes could be queued in withAltRepo that were intended to affect
the modified repo, but don't get flushed until later.
I don't know of any cases where either happens, but better safe than sorry.
Note that this affect withIndexFile, which is used in git-annex branch
updates. So, it potentially makes things slower. Should not be by much;
the overhead consists only of querying the current queue a couple of times,
and potentially flushing changes queued within withAltRepo earlier, that
could have maybe been bundled with other later changes.
Notice in particular that the existing queue is not flushed when calling
withAltRepo. So eg when git annex add needs to stage files in the index,
it will still bundle them together efficiently.
2016-06-03 17:48:14 +00:00
|
|
|
import qualified Annex.Queue
|
2020-06-17 19:13:52 +00:00
|
|
|
import qualified Utility.LockFile.PidLock as PidF
|
|
|
|
import qualified Utility.LockPool.PidLock as PidP
|
|
|
|
import Utility.LockPool (dropLock)
|
|
|
|
import Utility.Env
|
2020-07-01 15:21:10 +00:00
|
|
|
import Config
|
2016-04-06 19:33:29 +00:00
|
|
|
|
|
|
|
{- Runs an action using a different git index file. -}
|
2020-04-10 17:37:04 +00:00
|
|
|
withIndexFile :: AltIndexFile -> (FilePath -> Annex a) -> Annex a
|
|
|
|
withIndexFile i = withAltRepo usecachedgitenv restoregitenv
|
2016-09-29 17:36:48 +00:00
|
|
|
where
|
|
|
|
-- This is an optimisation. Since withIndexFile is run repeatedly,
|
2019-12-04 18:14:35 +00:00
|
|
|
-- typically with the same file, and addGitEnv uses the slow
|
|
|
|
-- getEnvironment when gitEnv is Nothing, and has to do a
|
|
|
|
-- nontrivial amount of work, we cache the modified environment
|
|
|
|
-- the first time, and reuse it in subsequent calls for the same
|
|
|
|
-- index file.
|
2016-09-29 17:36:48 +00:00
|
|
|
--
|
|
|
|
-- (This could be done at another level; eg when creating the
|
|
|
|
-- Git object in the first place, but it's more efficient to let
|
2019-12-04 18:14:35 +00:00
|
|
|
-- the environment be inherited in all calls to git where it
|
2016-09-29 17:36:48 +00:00
|
|
|
-- does not need to be modified.)
|
2020-04-10 17:37:04 +00:00
|
|
|
--
|
|
|
|
-- Also, the use of AltIndexFile avoids needing to construct
|
|
|
|
-- the FilePath each time, which saves enough time to be worth the
|
|
|
|
-- added complication.
|
|
|
|
usecachedgitenv g = case gitEnv g of
|
2019-12-04 18:14:35 +00:00
|
|
|
Nothing -> Annex.withState $ \s -> case Annex.cachedgitenv s of
|
2020-04-10 17:37:04 +00:00
|
|
|
Just (cachedi, cachedf, cachede) | i == cachedi ->
|
|
|
|
return (s, (g { gitEnv = Just cachede }, cachedf))
|
2019-12-04 18:14:35 +00:00
|
|
|
_ -> do
|
2020-04-10 17:37:04 +00:00
|
|
|
r@(g', f) <- addindex g
|
|
|
|
let cache = (,,)
|
|
|
|
<$> Just i
|
|
|
|
<*> Just f
|
|
|
|
<*> gitEnv g'
|
|
|
|
return (s { Annex.cachedgitenv = cache }, r)
|
|
|
|
Just _ -> liftIO $ addindex g
|
|
|
|
|
|
|
|
addindex g = do
|
|
|
|
f <- indexEnvVal $ case i of
|
|
|
|
AnnexIndexFile -> gitAnnexIndex g
|
|
|
|
ViewIndexFile -> gitAnnexViewIndex g
|
|
|
|
g' <- addGitEnv g indexEnv f
|
|
|
|
return (g', f)
|
|
|
|
|
|
|
|
restoregitenv g g' = g' { gitEnv = gitEnv g }
|
2016-04-06 19:33:29 +00:00
|
|
|
|
2016-04-06 21:32:04 +00:00
|
|
|
{- Runs an action using a different git work tree.
|
|
|
|
-
|
|
|
|
- Smudge and clean filters are disabled in this work tree. -}
|
2016-04-06 19:33:29 +00:00
|
|
|
withWorkTree :: FilePath -> Annex a -> Annex a
|
2020-04-10 17:37:04 +00:00
|
|
|
withWorkTree d a = withAltRepo
|
|
|
|
(\g -> return $ (g { location = modlocation (location g), gitGlobalOpts = gitGlobalOpts g ++ disableSmudgeConfig }, ()))
|
2016-04-06 21:32:04 +00:00
|
|
|
(\g g' -> g' { location = location g, gitGlobalOpts = gitGlobalOpts g })
|
2020-04-10 17:37:04 +00:00
|
|
|
(const a)
|
2016-04-06 19:33:29 +00:00
|
|
|
where
|
2019-12-09 17:49:05 +00:00
|
|
|
modlocation l@(Local {}) = l { worktree = Just (toRawFilePath d) }
|
2016-04-06 19:33:29 +00:00
|
|
|
modlocation _ = error "withWorkTree of non-local git repo"
|
2016-04-06 21:32:04 +00:00
|
|
|
disableSmudgeConfig = map Param
|
|
|
|
[ "-c", "filter.annex.smudge="
|
|
|
|
, "-c", "filter.annex.clean="
|
|
|
|
]
|
2016-04-06 19:33:29 +00:00
|
|
|
|
|
|
|
{- Runs an action with the git index file and HEAD, and a few other
|
|
|
|
- files that are related to the work tree coming from an overlay
|
|
|
|
- directory other than the usual. This is done by pointing
|
|
|
|
- GIT_COMMON_DIR at the regular git directory, and GIT_DIR at the
|
2016-04-22 16:29:32 +00:00
|
|
|
- overlay directory.
|
|
|
|
-
|
|
|
|
- Needs git 2.2.0 or newer.
|
|
|
|
-}
|
2016-04-06 19:33:29 +00:00
|
|
|
withWorkTreeRelated :: FilePath -> Annex a -> Annex a
|
2020-04-10 17:37:04 +00:00
|
|
|
withWorkTreeRelated d a = withAltRepo modrepo unmodrepo (const a)
|
2016-04-06 19:33:29 +00:00
|
|
|
where
|
2016-09-29 17:36:48 +00:00
|
|
|
modrepo g = liftIO $ do
|
2019-12-09 17:49:05 +00:00
|
|
|
g' <- addGitEnv g "GIT_COMMON_DIR"
|
|
|
|
=<< absPath (fromRawFilePath (localGitDir g))
|
2016-04-08 18:24:00 +00:00
|
|
|
g'' <- addGitEnv g' "GIT_DIR" d
|
2020-04-10 17:37:04 +00:00
|
|
|
return (g'' { gitEnvOverridesGitDir = True }, ())
|
2016-04-08 18:24:00 +00:00
|
|
|
unmodrepo g g' = g'
|
|
|
|
{ gitEnv = gitEnv g
|
|
|
|
, gitEnvOverridesGitDir = gitEnvOverridesGitDir g
|
|
|
|
}
|
2016-04-06 19:33:29 +00:00
|
|
|
|
|
|
|
withAltRepo
|
2020-04-10 17:37:04 +00:00
|
|
|
:: (Repo -> Annex (Repo, t))
|
2016-04-06 19:33:29 +00:00
|
|
|
-- ^ modify Repo
|
|
|
|
-> (Repo -> Repo -> Repo)
|
|
|
|
-- ^ undo modifications; first Repo is the original and second
|
|
|
|
-- is the one after running the action.
|
2020-04-10 17:37:04 +00:00
|
|
|
-> (t -> Annex a)
|
2016-04-06 19:33:29 +00:00
|
|
|
-> Annex a
|
|
|
|
withAltRepo modrepo unmodrepo a = do
|
|
|
|
g <- gitRepo
|
2020-04-10 17:37:04 +00:00
|
|
|
(g', t) <- modrepo g
|
withAltRepo needs a separate queue of changes
The queue could potentially contain changes from before withAltRepo, and
get flushed inside the call, which would apply the changes to the modified
repo.
Or, changes could be queued in withAltRepo that were intended to affect
the modified repo, but don't get flushed until later.
I don't know of any cases where either happens, but better safe than sorry.
Note that this affect withIndexFile, which is used in git-annex branch
updates. So, it potentially makes things slower. Should not be by much;
the overhead consists only of querying the current queue a couple of times,
and potentially flushing changes queued within withAltRepo earlier, that
could have maybe been bundled with other later changes.
Notice in particular that the existing queue is not flushed when calling
withAltRepo. So eg when git annex add needs to stage files in the index,
it will still bundle them together efficiently.
2016-06-03 17:48:14 +00:00
|
|
|
q <- Annex.Queue.get
|
|
|
|
v <- tryNonAsync $ do
|
|
|
|
Annex.changeState $ \s -> s
|
|
|
|
{ Annex.repo = g'
|
|
|
|
-- Start a separate queue for any changes made
|
|
|
|
-- with the modified repo.
|
|
|
|
, Annex.repoqueue = Nothing
|
|
|
|
}
|
2020-04-10 17:37:04 +00:00
|
|
|
a t
|
withAltRepo needs a separate queue of changes
The queue could potentially contain changes from before withAltRepo, and
get flushed inside the call, which would apply the changes to the modified
repo.
Or, changes could be queued in withAltRepo that were intended to affect
the modified repo, but don't get flushed until later.
I don't know of any cases where either happens, but better safe than sorry.
Note that this affect withIndexFile, which is used in git-annex branch
updates. So, it potentially makes things slower. Should not be by much;
the overhead consists only of querying the current queue a couple of times,
and potentially flushing changes queued within withAltRepo earlier, that
could have maybe been bundled with other later changes.
Notice in particular that the existing queue is not flushed when calling
withAltRepo. So eg when git annex add needs to stage files in the index,
it will still bundle them together efficiently.
2016-06-03 17:48:14 +00:00
|
|
|
void $ tryNonAsync Annex.Queue.flush
|
|
|
|
Annex.changeState $ \s -> s
|
|
|
|
{ Annex.repo = unmodrepo g (Annex.repo s)
|
|
|
|
, Annex.repoqueue = Just q
|
|
|
|
}
|
|
|
|
either E.throw return v
|
2020-06-17 19:13:52 +00:00
|
|
|
|
|
|
|
{- Wrap around actions that may run a git-annex child process.
|
|
|
|
-
|
|
|
|
- When pid locking is in use, this tries to take the pid lock, and if
|
|
|
|
- successful, holds it while running the child process. The action
|
|
|
|
- is run with the Annex monad modified so git commands are run with
|
|
|
|
- an env var set, which prevents child git annex processes from t
|
|
|
|
- rying to take the pid lock themselves.
|
|
|
|
-
|
|
|
|
- This way, any locking the parent does will not get in the way of
|
|
|
|
- the child. The child is assumed to not do any locking that conflicts
|
|
|
|
- with the parent, but if it did happen to do that, it would be noticed
|
|
|
|
- when git-annex is used without pid locking.
|
|
|
|
-}
|
|
|
|
runsGitAnnexChildProcess :: Annex a -> Annex a
|
|
|
|
runsGitAnnexChildProcess a = pidLockFile >>= \case
|
|
|
|
Nothing -> a
|
|
|
|
Just pidlock -> bracket (setup pidlock) cleanup (go pidlock)
|
|
|
|
where
|
|
|
|
setup pidlock = liftIO $ PidP.tryLock pidlock
|
|
|
|
|
|
|
|
cleanup (Just h) = liftIO $ dropLock h
|
|
|
|
cleanup Nothing = return ()
|
|
|
|
|
|
|
|
go _ Nothing = a
|
|
|
|
go pidlock (Just _h) = do
|
|
|
|
v <- liftIO $ PidF.pidLockEnv pidlock
|
|
|
|
let addenv g = do
|
|
|
|
g' <- liftIO $ addGitEnv g v "1"
|
|
|
|
return (g', ())
|
|
|
|
let rmenv oldg g
|
|
|
|
| any (\(k, _) -> k == v) (fromMaybe [] (Git.gitEnv oldg)) = g
|
|
|
|
| otherwise =
|
|
|
|
let e' = case Git.gitEnv g of
|
|
|
|
Just e -> Just (delEntry v e)
|
|
|
|
Nothing -> Nothing
|
|
|
|
in g { Git.gitEnv = e' }
|
|
|
|
withAltRepo addenv rmenv (const a)
|
|
|
|
|
|
|
|
runsGitAnnexChildProcess' :: Git.Repo -> (Git.Repo -> IO a) -> Annex a
|
|
|
|
runsGitAnnexChildProcess' r a = pidLockFile >>= \case
|
|
|
|
Nothing -> liftIO $ a r
|
|
|
|
Just pidlock -> liftIO $ bracket (setup pidlock) cleanup (go pidlock)
|
|
|
|
where
|
|
|
|
setup pidlock = PidP.tryLock pidlock
|
|
|
|
|
|
|
|
cleanup (Just h) = dropLock h
|
|
|
|
cleanup Nothing = return ()
|
|
|
|
|
|
|
|
go _ Nothing = a r
|
|
|
|
go pidlock (Just _h) = do
|
|
|
|
v <- PidF.pidLockEnv pidlock
|
|
|
|
r' <- addGitEnv r v "1"
|
|
|
|
a r'
|