- whether a repo is used as a submodule or not, and wheverever the
- submodule is mounted.
-
- - git-worktree directories have a .git file.
- - That needs to be converted to a symlink, and .git/annex made a symlink
- - to the main repository's git-annex directory.
+ - git-worktree directories have a .git file which points to a different
+ - git directory than the main git directory. That needs to be converted to
+ - a symlink, and .git/annex made a symlink to the main repository's
+ - git-annex directory so that annex symlinks in the git repository point
+ - to the object files. When the filesystem does not support symlinks, the
+ - mainWorkTreePath of the repository is set, so that the git-annex
+ - directory of the main repository will still be used.
+ -
- The worktree shares git config with the main repository, so the same
- annex uuid and other configuration will be used in the worktree as in
- the main repository.
- unlocked branches.
-
- Don't do any of this if the repo has not been initialized for git-annex
- - use yet.
+ - use yet. Except, do set mainWorkTreePath.
-}
fixupUnusualRepos :: Repo -> GitConfig -> IO Repo
fixupUnusualRepos r@(Repo { location = l@(Local { worktree = Just w, gitdir = d }) }) c
- | isNothing (annexVersion c) = return r
+ | isNothing (annexVersion c) =
+ ifM (needsGitLinkFixup r)
+ ( setworktreepath r
+ , return r
+ )
| needsSubmoduleFixup r = do
when (coreSymlinks c) $
(replacedotgit >> unsetcoreworktree)
}
| otherwise = ifM (needsGitLinkFixup r)
( do
- when (coreSymlinks c) $
- (replacedotgit >> worktreefixup)
- `catchNonAsync` \e -> hPutStrLn stderr $
- "warning: unable to convert .git file to symlink that will work with git-annex: " ++ show e
- return r'
+ if coreSymlinks c
+ then do
+ (replacedotgit >> worktreefixup)
+ `catchNonAsync` \e -> hPutStrLn stderr $
+ "warning: unable to convert .git file to symlink that will work with git-annex: " ++ show e
+ setworktreepath r'
+ else setworktreepath r'
, return r
)
where
-- Unsetting a config fails if it's not set, so ignore failure.
unsetcoreworktree = void $ Git.Config.unset "core.worktree" r
+
+ -- git-worktree sets up a "commondir" file that contains
+ -- the path to the main git directory.
+ -- Using --separate-git-dir does not.
+ commondirfile = fromOsPath (d </> literalOsPath "commondir")
- worktreefixup = do
- -- git-worktree sets up a "commondir" file that contains
- -- the path to the main git directory.
- -- Using --separate-git-dir does not.
- let commondirfile = fromOsPath (d </> literalOsPath "commondir")
- catchDefaultIO Nothing (headMaybe . lines <$> readFile commondirfile) >>= \case
- Just gd -> do
- -- Make the worktree's git directory
- -- contain an annex symlink to the main
- -- repository's annex directory.
- let linktarget = toOsPath gd </> literalOsPath "annex"
- R.createSymbolicLink (fromOsPath linktarget) $
- fromOsPath $ dotgit </> literalOsPath "annex"
- Nothing -> return ()
+ readcommondirfile = catchDefaultIO Nothing $
+ fmap toOsPath . headMaybe . lines
+ <$> readFile commondirfile
+
+ setworktreepath r'' = readcommondirfile >>= \case
+ Just gd -> return $ r''
+ { mainWorkTreePath = Just gd
+ }
+ Nothing -> return r''
+
+ worktreefixup = readcommondirfile >>= \case
+ Just gd -> do
+ -- Make the worktree's git directory
+ -- contain an annex symlink to the main
+ -- repository's annex directory.
+ let linktarget = gd </> literalOsPath "annex"
+ R.createSymbolicLink (fromOsPath linktarget) $
+ fromOsPath $ dotgit </> literalOsPath "annex"
+ Nothing -> return ()
-- Repo adjusted, so that symlinks to objects that get checked
-- in will have the usual path, rather than pointing off to the
{- git-annex file locations
-
- - Copyright 2010-2024 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2025 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
{-# LANGUAGE CPP #-}
module Annex.Locations (
+ GitLocationMaker(..),
+ standardGitLocationMaker,
+ repoGitLocationMaker,
keyFile,
fileKey,
keyPaths,
import Annex.DirHashes
import Annex.Fixup
+{- When constructing a path that is usually relative to the
+ - .git directory, this can be used to relocate the path to
+ - elsewhere.
+ -
+ - This is used when in a linked git worktree, which has its own
+ - git directory, to make the git-annex directory be located in the
+ - git directory of the main worktree.
+ -}
+newtype GitLocationMaker = GitLocationMaker (OsPath -> OsPath)
+
+standardGitLocationMaker :: GitLocationMaker
+standardGitLocationMaker = GitLocationMaker id
+
+repoGitLocationMaker :: Git.Repo -> GitLocationMaker
+repoGitLocationMaker r = case Git.mainWorkTreePath r of
+ Nothing -> standardGitLocationMaker
+ Just p -> GitLocationMaker (p </>)
+
{- Conventions:
-
- Functions ending in "Dir" should always return values ending with a
{- The directory git annex uses for local state, relative to the .git
- directory -}
-annexDir :: OsPath
-annexDir = addTrailingPathSeparator (literalOsPath "annex")
+annexDir :: GitLocationMaker -> OsPath
+annexDir (GitLocationMaker glm) = addTrailingPathSeparator $
+ glm $ literalOsPath "annex"
{- The directory git annex uses for locally available object content,
- relative to the .git directory -}
-objectDir :: OsPath
-objectDir = addTrailingPathSeparator $ annexDir </> literalOsPath "objects"
+objectDir :: GitLocationMaker -> OsPath
+objectDir glm = addTrailingPathSeparator $
+ annexDir glm </> literalOsPath "objects"
{- Annexed file's possible locations relative to the .git directory
- in a non-bare repository.
- Normally it is hashDirMixed. However, it's always possible that a
- bare repository was converted to non-bare, or that the cripped
- filesystem setting changed, so still need to check both. -}
-annexLocationsNonBare :: GitConfig -> Key -> [OsPath]
-annexLocationsNonBare config key =
- map (annexLocation config key) [hashDirMixed, hashDirLower]
+annexLocationsNonBare :: GitLocationMaker -> GitConfig -> Key -> [OsPath]
+annexLocationsNonBare glm config key =
+ map (annexLocation glm config key) [hashDirMixed, hashDirLower]
{- Annexed file's possible locations relative to a bare repository. -}
-annexLocationsBare :: GitConfig -> Key -> [OsPath]
-annexLocationsBare config key =
- map (annexLocation config key) [hashDirLower, hashDirMixed]
+annexLocationsBare :: GitLocationMaker -> GitConfig -> Key -> [OsPath]
+annexLocationsBare glm config key =
+ map (annexLocation glm config key) [hashDirLower, hashDirMixed]
-annexLocation :: GitConfig -> Key -> (HashLevels -> Hasher) -> OsPath
-annexLocation config key hasher = objectDir </> keyPath key (hasher $ objectHashLevels config)
+annexLocation :: GitLocationMaker -> GitConfig -> Key -> (HashLevels -> Hasher) -> OsPath
+annexLocation glm config key hasher =
+ objectDir glm </> keyPath key (hasher $ objectHashLevels config)
{- For exportree remotes with annexobjects=true, objects are stored
- in this location as well as in the exported tree. -}
exportAnnexObjectLocation :: GitConfig -> Key -> ExportLocation
exportAnnexObjectLocation gc k =
mkExportLocation $
- literalOsPath ".git" </> annexLocation gc k hashDirLower
+ literalOsPath ".git"
+ </> annexLocation standardGitLocationMaker gc k hashDirLower
{- Number of subdirectories from the gitAnnexObjectDir
- to the gitAnnexLocation. -}
gitAnnexLocation = gitAnnexLocation' doesPathExist
gitAnnexLocation' :: (OsPath -> IO Bool) -> Key -> Git.Repo -> GitConfig -> IO OsPath
-gitAnnexLocation' checker key r config = gitAnnexLocation'' key r config
- (annexCrippledFileSystem config)
- (coreSymlinks config)
- checker
- (Git.localGitDir r)
-
-gitAnnexLocation'' :: Key -> Git.Repo -> GitConfig -> Bool -> Bool -> (OsPath -> IO Bool) -> OsPath -> IO OsPath
-gitAnnexLocation'' key r config crippled symlinkssupported checker gitdir
+gitAnnexLocation' checker key r config =
+ gitAnnexLocation'' key glm r config
+ (annexCrippledFileSystem config)
+ (coreSymlinks config)
+ checker
+ (Git.localGitDir r)
+ where
+ glm = repoGitLocationMaker r
+
+gitAnnexLocation'' :: Key -> GitLocationMaker -> Git.Repo -> GitConfig -> Bool -> Bool -> (OsPath -> IO Bool) -> OsPath -> IO OsPath
+gitAnnexLocation'' key glm r config crippled symlinkssupported checker gitdir
{- Bare repositories default to hashDirLower for new
- content, as it's more portable. But check all locations. -}
| Git.repoIsLocalBare r = checkall annexLocationsBare
else checkall annexLocationsBare
| otherwise = checkall annexLocationsNonBare
where
- only = return . inrepo . annexLocation config key
- checkall f = check $ map inrepo $ f config key
+ only = return . inrepo . annexLocation glm config key
+ checkall f = check $ map inrepo $ f glm config key
inrepo d = gitdir </> d
check locs@(l:_) = fromMaybe l <$> firstM checker locs
currdir <- getCurrentDirectory
let absfile = absNormPathUnix currdir file
let gitdir = getgitdir currdir
- loc <- gitAnnexLocation'' key r config False False (\_ -> return True) gitdir
+ loc <- gitAnnexLocation'' key standardGitLocationMaker r config False False (\_ -> return True) gitdir
toInternalGitPath <$> relPathDirToFile (parentDir absfile) loc
where
getgitdir currdir
gitAnnexInodeSentinal r = gitAnnexDir r </> literalOsPath "sentinal"
gitAnnexInodeSentinalCache :: Git.Repo -> OsPath
-gitAnnexInodeSentinalCache r = gitAnnexInodeSentinal r <> literalOsPath ".cache"
+gitAnnexInodeSentinalCache r =
+ gitAnnexInodeSentinal r <> literalOsPath ".cache"
{- The annex directory of a repository. -}
gitAnnexDir :: Git.Repo -> OsPath
-gitAnnexDir r = addTrailingPathSeparator $ Git.localGitDir r </> annexDir
+gitAnnexDir r = addTrailingPathSeparator $
+ Git.localGitDir r </> annexDir glm
+ where
+ glm = repoGitLocationMaker r
{- The part of the annex directory where file contents are stored. -}
gitAnnexObjectDir :: Git.Repo -> OsPath
gitAnnexObjectDir r = addTrailingPathSeparator $
- Git.localGitDir r </> objectDir
+ Git.localGitDir r </> objectDir glm
+ where
+ glm = repoGitLocationMaker r
{- .git/annex/tmp/ is used for temp files for key's contents -}
gitAnnexTmpObjectDir :: Git.Repo -> OsPath
{- The temp file to use for a given key's content. -}
gitAnnexTmpObjectLocation :: Key -> Git.Repo -> OsPath
-gitAnnexTmpObjectLocation key r = gitAnnexTmpObjectDir r </> keyFile key
+gitAnnexTmpObjectLocation key r =
+ gitAnnexTmpObjectDir r </> keyFile key
{- Given a temp file such as gitAnnexTmpObjectLocation, makes a name for a
- subdirectory in the same location, that can be used as a work area
{- Lock file for the keys database. -}
gitAnnexKeysDbLock :: Git.Repo -> GitConfig -> OsPath
-gitAnnexKeysDbLock r c = gitAnnexKeysDbDir r c <> literalOsPath ".lck"
+gitAnnexKeysDbLock r c = gitAnnexKeysDbDir r c <> literalOsPath ".lck"
{- Contains the stat of the last index file that was
- reconciled with the keys database. -}
gitAnnexKeysDbIndexCache :: Git.Repo -> GitConfig -> OsPath
-gitAnnexKeysDbIndexCache r c =
- gitAnnexKeysDbDir r c <> literalOsPath ".cache"
+gitAnnexKeysDbIndexCache r c = gitAnnexKeysDbDir r c <> literalOsPath ".cache"
{- .git/annex/fsck/uuid/ is used to store information about incremental
- fscks. -}
{- used to store information about incremental fscks. -}
gitAnnexFsckState :: UUID -> Git.Repo -> OsPath
-gitAnnexFsckState u r = gitAnnexFsckDir u r Nothing </> literalOsPath "state"
+gitAnnexFsckState u r =
+ gitAnnexFsckDir u r Nothing </> literalOsPath "state"
{- Directory containing database used to record fsck info. -}
gitAnnexFsckDbDir :: UUID -> Git.Repo -> GitConfig -> OsPath
-gitAnnexFsckDbDir u r c = gitAnnexFsckDir u r (Just c) </> literalOsPath "fsckdb"
+gitAnnexFsckDbDir u r c =
+ gitAnnexFsckDir u r (Just c) </> literalOsPath "fsckdb"
{- Directory containing old database used to record fsck info. -}
gitAnnexFsckDbDirOld :: UUID -> Git.Repo -> GitConfig -> OsPath
-gitAnnexFsckDbDirOld u r c = gitAnnexFsckDir u r (Just c) </> literalOsPath "db"
+gitAnnexFsckDbDirOld u r c =
+ gitAnnexFsckDir u r (Just c) </> literalOsPath "db"
{- Lock file for the fsck database. -}
gitAnnexFsckDbLock :: UUID -> Git.Repo -> GitConfig -> OsPath
-gitAnnexFsckDbLock u r c = gitAnnexFsckDir u r (Just c) </> literalOsPath "fsck.lck"
+gitAnnexFsckDbLock u r c =
+ gitAnnexFsckDir u r (Just c) </> literalOsPath "fsck.lck"
{- .git/annex/fsckresults/uuid is used to store results of git fscks -}
gitAnnexFsckResultsLog :: UUID -> Git.Repo -> OsPath