convert TopFilePath to use RawFilePath

Adds a dependency on filepath-bytestring, an as yet unreleased fork of
filepath that operates on RawFilePath.

Git.Repo also changed to use RawFilePath for the path to the repo.

This does eliminate some RawFilePath -> FilePath -> RawFilePath
conversions. And filepath-bytestring's </> is probably faster.
But I don't expect a major performance improvement from this.
This is mostly groundwork for making Annex.Location use RawFilePath,
which will allow for a conversion-free pipleline.
This commit is contained in:
Joey Hess 2019-12-09 13:49:05 -04:00
parent a7004375ec
commit bdec7fed9c
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
97 changed files with 323 additions and 271 deletions

View file

@ -24,10 +24,10 @@ gitCommandLine params r@(Repo { location = l@(Local { } ) }) =
where
setdir
| gitEnvOverridesGitDir r = []
| otherwise = [Param $ "--git-dir=" ++ gitdir l]
| otherwise = [Param $ "--git-dir=" ++ fromRawFilePath (gitdir l)]
settree = case worktree l of
Nothing -> []
Just t -> [Param $ "--work-tree=" ++ t]
Just t -> [Param $ "--work-tree=" ++ fromRawFilePath t]
gitCommandLine _ repo = assertLocal repo $ error "internal"
{- Runs git in the specified repo. -}

View file

@ -13,6 +13,7 @@ import qualified Data.Map as M
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import Data.Char
import qualified System.FilePath.ByteString as P
import Common
import Git
@ -61,7 +62,7 @@ read' repo = go repo
where
params = ["config", "--null", "--list"]
p = (proc "git" params)
{ cwd = Just d
{ cwd = Just (fromRawFilePath d)
, env = gitEnv repo
}
@ -114,13 +115,13 @@ store' k v repo = repo
-}
updateLocation :: Repo -> IO Repo
updateLocation r@(Repo { location = LocalUnknown d })
| isBare r = ifM (doesDirectoryExist dotgit)
| isBare r = ifM (doesDirectoryExist (fromRawFilePath dotgit))
( updateLocation' r $ Local dotgit Nothing
, updateLocation' r $ Local d Nothing
)
| otherwise = updateLocation' r $ Local dotgit (Just d)
where
dotgit = (d </> ".git")
dotgit = d P.</> ".git"
updateLocation r@(Repo { location = l@(Local {}) }) = updateLocation' r l
updateLocation r = return r
@ -130,9 +131,9 @@ updateLocation' r l = do
Nothing -> return l
Just (ConfigValue d) -> do
{- core.worktree is relative to the gitdir -}
top <- absPath $ gitdir l
top <- absPath $ fromRawFilePath (gitdir l)
let p = absPathFrom top (fromRawFilePath d)
return $ l { worktree = Just p }
return $ l { worktree = Just (toRawFilePath p) }
return $ r { location = l' }
{- Parses git config --list or git config --null --list output into a

View file

@ -62,7 +62,7 @@ fromAbsPath dir
| otherwise =
error $ "internal error, " ++ dir ++ " is not absolute"
where
ret = pure . newFrom . LocalUnknown
ret = pure . newFrom . LocalUnknown . toRawFilePath
canondir = dropTrailingPathSeparator dir
{- When dir == "foo/.git", git looks for "foo/.git/.git",
- and failing that, uses "foo" as the repository. -}
@ -117,7 +117,7 @@ localToUrl reference r
[ Url.scheme reference
, "//"
, auth
, repoPath r
, fromRawFilePath (repoPath r)
]
in r { location = Url $ fromJust $ parseURI absurl }
@ -154,7 +154,7 @@ fromRemoteLocation s repo = gen $ parseRemoteLocation s repo
fromRemotePath :: FilePath -> Repo -> IO Repo
fromRemotePath dir repo = do
dir' <- expandTilde dir
fromPath $ repoPath repo </> dir'
fromPath $ fromRawFilePath (repoPath repo) </> dir'
{- Git remotes can have a directory that is specified relative
- to the user's home directory, or that contains tilde expansions.
@ -204,7 +204,7 @@ checkForRepo dir =
where
check test cont = maybe cont (return . Just) =<< test
checkdir c = ifM c
( return $ Just $ LocalUnknown dir
( return $ Just $ LocalUnknown $ toRawFilePath dir
, return Nothing
)
isRepo = checkdir $
@ -224,9 +224,9 @@ checkForRepo dir =
catchDefaultIO "" (readFile $ dir </> ".git")
return $ if gitdirprefix `isPrefixOf` c
then Just $ Local
{ gitdir = absPathFrom dir $
{ gitdir = toRawFilePath $ absPathFrom dir $
drop (length gitdirprefix) c
, worktree = Just dir
, worktree = Just (toRawFilePath dir)
}
else Nothing
where

View file

@ -37,7 +37,7 @@ get = do
gd <- getpathenv "GIT_DIR"
r <- configure gd =<< fromCwd
prefix <- getpathenv "GIT_PREFIX"
wt <- maybe (worktree $ location r) Just
wt <- maybe (fromRawFilePath <$> worktree (location r)) Just
<$> getpathenvprefix "GIT_WORK_TREE" prefix
case wt of
Nothing -> return r
@ -68,13 +68,18 @@ get = do
absd <- absPath d
curr <- getCurrentDirectory
r <- Git.Config.read $ newFrom $
Local { gitdir = absd, worktree = Just curr }
Local
{ gitdir = toRawFilePath absd
, worktree = Just (toRawFilePath curr)
}
return $ if Git.Config.isBare r
then r { location = (location r) { worktree = Nothing } }
else r
configure Nothing Nothing = giveup "Not in a git repository."
addworktree w r = changelocation r $
Local { gitdir = gitdir (location r), worktree = w }
addworktree w r = changelocation r $ Local
{ gitdir = gitdir (location r)
, worktree = fmap toRawFilePath w
}
changelocation r l = r { location = l }

View file

@ -31,9 +31,9 @@ import qualified Git.Ref
{- Checks if the DiffTreeItem modifies a file with a given name
- or under a directory by that name. -}
isDiffOf :: DiffTreeItem -> TopFilePath -> Bool
isDiffOf diff f = case getTopFilePath f of
isDiffOf diff f = case fromRawFilePath (getTopFilePath f) of
"" -> True -- top of repo contains all
d -> d `dirContains` getTopFilePath (file diff)
d -> d `dirContains` fromRawFilePath (getTopFilePath (file diff))
{- Diffs two tree Refs. -}
diffTree :: Ref -> Ref -> Repo -> IO ([DiffTreeItem], IO Bool)
@ -113,7 +113,7 @@ parseDiffRaw l = go l
, srcsha = fromMaybe (error "bad srcsha") $ extractSha ssha
, dstsha = fromMaybe (error "bad dstsha") $ extractSha dsha
, status = s
, file = asTopFilePath $ fromRawFilePath $ fromInternalGitPath $ Git.Filename.decode $ toRawFilePath f
, file = asTopFilePath $ fromInternalGitPath $ Git.Filename.decode $ toRawFilePath f
}
where
readmode = fst . Prelude.head . readOct

View file

@ -30,8 +30,10 @@ addGitEnv g var val = adjustGitEnv g (addEntry var val)
- and a copy of the rest of the system environment. -}
propGitEnv :: Repo -> IO [(String, String)]
propGitEnv g = do
g' <- addGitEnv g "GIT_DIR" (localGitDir g)
g'' <- maybe (pure g') (addGitEnv g' "GIT_WORK_TREE") (repoWorkTree g)
g' <- addGitEnv g "GIT_DIR" (fromRawFilePath (localGitDir g))
g'' <- maybe (pure g')
(addGitEnv g' "GIT_WORK_TREE" . fromRawFilePath)
(repoWorkTree g)
return $ fromMaybe [] (gitEnv g'')
{- Use with any action that makes a commit to set metadata. -}

View file

@ -5,7 +5,7 @@
- top of the repository even when run in a subdirectory. Adding some
- types helps keep that straight.
-
- Copyright 2012-2013 Joey Hess <id@joeyh.name>
- Copyright 2012-2019 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
@ -31,13 +31,14 @@ module Git.FilePath (
import Common
import Git
import qualified System.FilePath.Posix
import qualified System.FilePath.ByteString as P
import qualified System.FilePath.Posix.ByteString
import GHC.Generics
import Control.DeepSeq
import qualified Data.ByteString as S
{- A RawFilePath, relative to the top of the git repository. -}
newtype TopFilePath = TopFilePath { getTopFilePath :: FilePath }
newtype TopFilePath = TopFilePath { getTopFilePath :: RawFilePath }
deriving (Show, Eq, Ord, Generic)
instance NFData TopFilePath
@ -49,19 +50,20 @@ data BranchFilePath = BranchFilePath Ref TopFilePath
{- Git uses the branch:file form to refer to a BranchFilePath -}
descBranchFilePath :: BranchFilePath -> S.ByteString
descBranchFilePath (BranchFilePath b f) =
encodeBS' (fromRef b) <> ":" <> toRawFilePath (getTopFilePath f)
encodeBS' (fromRef b) <> ":" <> getTopFilePath f
{- Path to a TopFilePath, within the provided git repo. -}
fromTopFilePath :: TopFilePath -> Git.Repo -> FilePath
fromTopFilePath p repo = combine (repoPath repo) (getTopFilePath p)
fromTopFilePath :: TopFilePath -> Git.Repo -> RawFilePath
fromTopFilePath p repo = P.combine (repoPath repo) (getTopFilePath p)
{- The input FilePath can be absolute, or relative to the CWD. -}
toTopFilePath :: FilePath -> Git.Repo -> IO TopFilePath
toTopFilePath file repo = TopFilePath <$> relPathDirToFile (repoPath repo) file
toTopFilePath :: RawFilePath -> Git.Repo -> IO TopFilePath
toTopFilePath file repo = TopFilePath . toRawFilePath
<$> relPathDirToFile (fromRawFilePath (repoPath repo)) (fromRawFilePath file)
{- The input FilePath must already be relative to the top of the git
{- The input RawFilePath must already be relative to the top of the git
- repository -}
asTopFilePath :: FilePath -> TopFilePath
asTopFilePath :: RawFilePath -> TopFilePath
asTopFilePath file = TopFilePath file
{- Git may use a different representation of a path when storing
@ -91,5 +93,5 @@ fromInternalGitPath = encodeBS . replace "/" "\\" . decodeBS
- so try posix paths.
-}
absoluteGitPath :: RawFilePath -> Bool
absoluteGitPath p = isAbsolute (decodeBS p) ||
System.FilePath.Posix.isAbsolute (decodeBS (toInternalGitPath p))
absoluteGitPath p = P.isAbsolute p ||
System.FilePath.Posix.ByteString.isAbsolute (toInternalGitPath p)

View file

@ -28,7 +28,7 @@ instance Eq Hook where
a == b = hookName a == hookName b
hookFile :: Hook -> Repo -> FilePath
hookFile h r = localGitDir r </> "hooks" </> hookName h
hookFile h r = fromRawFilePath (localGitDir r) </> "hooks" </> hookName h
{- Writes a hook. Returns False if the hook already exists with a different
- content. Upgrades old scripts.

View file

@ -49,7 +49,7 @@ override index _r = do
{- The normal index file. Does not check GIT_INDEX_FILE. -}
indexFile :: Repo -> FilePath
indexFile r = localGitDir r </> "index"
indexFile r = fromRawFilePath (localGitDir r) </> "index"
{- The index file git will currently use, checking GIT_INDEX_FILE. -}
currentIndexFile :: Repo -> IO FilePath

View file

@ -185,7 +185,7 @@ typeChanged' ps l repo = do
(fs, cleanup) <- pipeNullSplit (prefix ++ ps ++ suffix) repo
-- git diff returns filenames relative to the top of the git repo;
-- convert to filenames relative to the cwd, like git ls-files.
top <- absPath (repoPath repo)
top <- absPath (fromRawFilePath (repoPath repo))
currdir <- getCurrentDirectory
return (map (\f -> toRawFilePath (relPathDirToFileAbs currdir $ top </> decodeBL' f)) fs, cleanup)
where

View file

@ -100,7 +100,7 @@ parserLsTree = TreeItem
<*> (Ref . decodeBS' <$> A.take shaSize)
<* A8.char '\t'
-- file
<*> (asTopFilePath . decodeBS' . Git.Filename.decode <$> A.takeByteString)
<*> (asTopFilePath . Git.Filename.decode <$> A.takeByteString)
{- Inverse of parseLsTree -}
formatLsTree :: TreeItem -> String
@ -108,5 +108,5 @@ formatLsTree ti = unwords
[ showOct (mode ti) ""
, decodeBS (typeobj ti)
, fromRef (sha ti)
, getTopFilePath (file ti)
, fromRawFilePath (getTopFilePath (file ti))
]

View file

@ -12,7 +12,7 @@ import Git
import Git.Sha
objectsDir :: Repo -> FilePath
objectsDir r = localGitDir r </> "objects"
objectsDir r = fromRawFilePath (localGitDir r) </> "objects"
packDir :: Repo -> FilePath
packDir r = objectsDir r </> "pack"

View file

@ -22,7 +22,7 @@ headRef :: Ref
headRef = Ref "HEAD"
headFile :: Repo -> FilePath
headFile r = localGitDir r </> "HEAD"
headFile r = fromRawFilePath (localGitDir r) </> "HEAD"
setHeadRef :: Ref -> Repo -> IO ()
setHeadRef ref r = writeFile (headFile r) ("ref: " ++ fromRef ref)
@ -85,7 +85,7 @@ exists ref = runBool
{- The file used to record a ref. (Git also stores some refs in a
- packed-refs file.) -}
file :: Ref -> Repo -> FilePath
file ref repo = localGitDir repo </> fromRef ref
file ref repo = fromRawFilePath (localGitDir repo) </> fromRef ref
{- Checks if HEAD exists. It generally will, except for in a repository
- that was just created. -}

View file

@ -227,7 +227,7 @@ badBranches missing r = filterM isbad =<< getAllRefs r
- Relies on packed refs being exploded before it's called.
-}
getAllRefs :: Repo -> IO [Ref]
getAllRefs r = getAllRefs' (localGitDir r </> "refs")
getAllRefs r = getAllRefs' (fromRawFilePath (localGitDir r) </> "refs")
getAllRefs' :: FilePath -> IO [Ref]
getAllRefs' refdir = do
@ -245,13 +245,13 @@ explodePackedRefsFile r = do
nukeFile f
where
makeref (sha, ref) = do
let dest = localGitDir r </> fromRef ref
let dest = fromRawFilePath (localGitDir r) </> fromRef ref
createDirectoryIfMissing True (parentDir dest)
unlessM (doesFileExist dest) $
writeFile dest (fromRef sha)
packedRefsFile :: Repo -> FilePath
packedRefsFile r = localGitDir r </> "packed-refs"
packedRefsFile r = fromRawFilePath (localGitDir r) </> "packed-refs"
parsePacked :: String -> Maybe (Sha, Ref)
parsePacked l = case words l of
@ -263,7 +263,7 @@ parsePacked l = case words l of
{- git-branch -d cannot be used to remove a branch that is directly
- pointing to a corrupt commit. -}
nukeBranchRef :: Branch -> Repo -> IO ()
nukeBranchRef b r = nukeFile $ localGitDir r </> fromRef b
nukeBranchRef b r = nukeFile $ fromRawFilePath (localGitDir r) </> fromRef b
{- Finds the most recent commit to a branch that does not need any
- of the missing objects. If the input branch is good as-is, returns it.
@ -366,16 +366,16 @@ checkIndex r = do
- itself is not corrupt. -}
checkIndexFast :: Repo -> IO Bool
checkIndexFast r = do
(indexcontents, cleanup) <- LsFiles.stagedDetails [toRawFilePath (repoPath r)] r
(indexcontents, cleanup) <- LsFiles.stagedDetails [repoPath r] r
length indexcontents `seq` cleanup
missingIndex :: Repo -> IO Bool
missingIndex r = not <$> doesFileExist (localGitDir r </> "index")
missingIndex r = not <$> doesFileExist (fromRawFilePath (localGitDir r) </> "index")
{- Finds missing and ok files staged in the index. -}
partitionIndex :: Repo -> IO ([LsFiles.StagedDetails], [LsFiles.StagedDetails], IO Bool)
partitionIndex r = do
(indexcontents, cleanup) <- LsFiles.stagedDetails [toRawFilePath (repoPath r)] r
(indexcontents, cleanup) <- LsFiles.stagedDetails [repoPath r] r
l <- forM indexcontents $ \i -> case i of
(_file, Just sha, Just _mode) -> (,) <$> isMissing sha r <*> pure i
_ -> pure (False, i)
@ -446,7 +446,7 @@ preRepair g = do
let f = indexFile g
void $ tryIO $ allowWrite f
where
headfile = localGitDir g </> "HEAD"
headfile = fromRawFilePath (localGitDir g) </> "HEAD"
validhead s = "ref: refs/" `isPrefixOf` s || isJust (extractSha s)
{- Put it all together. -}

View file

@ -57,13 +57,13 @@ parseStatusZ = go []
in go (v : c) xs'
_ -> go c xs
cparse 'M' f _ = (Just (Modified (asTopFilePath f)), Nothing)
cparse 'A' f _ = (Just (Added (asTopFilePath f)), Nothing)
cparse 'D' f _ = (Just (Deleted (asTopFilePath f)), Nothing)
cparse 'T' f _ = (Just (TypeChanged (asTopFilePath f)), Nothing)
cparse '?' f _ = (Just (Untracked (asTopFilePath f)), Nothing)
cparse 'M' f _ = (Just (Modified (asTopFilePath (toRawFilePath f))), Nothing)
cparse 'A' f _ = (Just (Added (asTopFilePath (toRawFilePath f))), Nothing)
cparse 'D' f _ = (Just (Deleted (asTopFilePath (toRawFilePath f))), Nothing)
cparse 'T' f _ = (Just (TypeChanged (asTopFilePath (toRawFilePath f))), Nothing)
cparse '?' f _ = (Just (Untracked (asTopFilePath (toRawFilePath f))), Nothing)
cparse 'R' f (oldf:xs) =
(Just (Renamed (asTopFilePath oldf) (asTopFilePath f)), Just xs)
(Just (Renamed (asTopFilePath (toRawFilePath oldf)) (asTopFilePath (toRawFilePath f))), Just xs)
cparse _ _ _ = (Nothing, Nothing)
getStatus :: [CommandParam] -> [FilePath] -> Repo -> IO ([StagedUnstaged Status], IO Bool)

View file

@ -119,7 +119,7 @@ mkTreeOutput fm ot s f = concat
, " "
, fromRef s
, "\t"
, takeFileName (getTopFilePath f)
, takeFileName (fromRawFilePath (getTopFilePath f))
, "\NUL"
]
@ -156,7 +156,7 @@ treeItemsToTree = go M.empty
Just (NewSubTree d l) ->
go (addsubtree idir m (NewSubTree d (c:l))) is
_ ->
go (addsubtree idir m (NewSubTree (asTopFilePath idir) [c])) is
go (addsubtree idir m (NewSubTree (asTopFilePath (toRawFilePath idir)) [c])) is
where
p = gitPath i
idir = takeDirectory p
@ -169,7 +169,7 @@ treeItemsToTree = go M.empty
Just (NewSubTree d' l) ->
let l' = filter (\ti -> gitPath ti /= d) l
in addsubtree parent m' (NewSubTree d' (t:l'))
_ -> addsubtree parent m' (NewSubTree (asTopFilePath parent) [t])
_ -> addsubtree parent m' (NewSubTree (asTopFilePath (toRawFilePath parent)) [t])
| otherwise = M.insert d t m
where
parent = takeDirectory d
@ -328,7 +328,7 @@ graftTree' subtree graftloc basetree repo hdl = go basetree graftdirs
-- For a graftloc of "foo/bar/baz", this generates
-- ["foo", "foo/bar", "foo/bar/baz"]
graftdirs = map (asTopFilePath . decodeBS . toInternalGitPath . encodeBS) $
graftdirs = map (asTopFilePath . toInternalGitPath . encodeBS) $
mkpaths [] $ splitDirectories $ gitPath graftloc
mkpaths _ [] = []
mkpaths base (d:rest) = (joinPath base </> d) : mkpaths (base ++ [d]) rest
@ -366,7 +366,7 @@ instance GitPath FilePath where
gitPath = id
instance GitPath TopFilePath where
gitPath = getTopFilePath
gitPath = fromRawFilePath . getTopFilePath
instance GitPath TreeItem where
gitPath (TreeItem f _ _) = gitPath f

View file

@ -30,8 +30,8 @@ import Utility.FileSystemEncoding
- else known about it.
-}
data RepoLocation
= Local { gitdir :: FilePath, worktree :: Maybe FilePath }
| LocalUnknown FilePath
= Local { gitdir :: RawFilePath, worktree :: Maybe RawFilePath }
| LocalUnknown RawFilePath
| Url URI
| Unknown
deriving (Show, Eq, Ord)

View file

@ -91,7 +91,7 @@ mergeFile info file hashhandle h = case filter (/= nullSha) [Ref asha, Ref bsha]
where
[_colonmode, _bmode, asha, bsha, _status] = words info
use sha = return $ Just $
updateIndexLine sha TreeFile $ asTopFilePath file
updateIndexLine sha TreeFile $ asTopFilePath $ toRawFilePath file
-- Get file and split into lines to union merge.
-- The encoding of the file is assumed to be either ASCII or utf-8;
-- in either case it's safe to split on \n

View file

@ -96,13 +96,13 @@ updateIndexLine sha treeitemtype file = L.fromStrict $
stageFile :: Sha -> TreeItemType -> FilePath -> Repo -> IO Streamer
stageFile sha treeitemtype file repo = do
p <- toTopFilePath file repo
p <- toTopFilePath (toRawFilePath file) repo
return $ pureStreamer $ updateIndexLine sha treeitemtype p
{- A streamer that removes a file from the index. -}
unstageFile :: FilePath -> Repo -> IO Streamer
unstageFile file repo = do
p <- toTopFilePath file repo
p <- toTopFilePath (toRawFilePath file) repo
return $ unstageFile' p
unstageFile' :: TopFilePath -> Streamer
@ -118,7 +118,7 @@ stageSymlink file sha repo = do
!line <- updateIndexLine
<$> pure sha
<*> pure TreeSymlink
<*> toTopFilePath file repo
<*> toTopFilePath (toRawFilePath file) repo
return $ pureStreamer line
{- A streamer that applies a DiffTreeItem to the index. -}
@ -128,7 +128,7 @@ stageDiffTreeItem d = case toTreeItemType (Diff.dstmode d) of
Just t -> pureStreamer $ updateIndexLine (Diff.dstsha d) t (Diff.file d)
indexPath :: TopFilePath -> InternalGitPath
indexPath = toInternalGitPath . toRawFilePath . getTopFilePath
indexPath = toInternalGitPath . getTopFilePath
{- Refreshes the index, by checking file stat information. -}
refreshIndex :: Repo -> ((FilePath -> IO ()) -> IO ()) -> IO Bool