fileRef: make paths relative and simplified
authorJoey Hess <joeyh@joeyh.name>
Fri, 7 May 2021 17:25:59 +0000 (13:25 -0400)
committerJoey Hess <joeyh@joeyh.name>
Fri, 7 May 2021 17:25:59 +0000 (13:25 -0400)
Fix behavior of several commands, including reinject, addurl, and rmurl
when given an absolute path to an unlocked file, or a relative path that
leaves and re-enters the repository.

To avoid slowing down all the cases where the paths are already ok
with an unncessary call to getCurrentDirectory, put in an optimisation
in relPathCwdToFile. That will probably also speed up other parts of
git-annex by some small amount, but I have not benchmarked.

Note that I did not convert branchFileRef, because it seems likely that
it will be used with a file that is not provided by the user, so is already
in a sane format. This is certainly true for the way git-annex uses it,
though maybe arguable to the extent Git.Ref is a reusable library.

Annex/CatFile.hs
CHANGELOG
Command/Smudge.hs
Git/Ref.hs
Utility/Path/AbsRel.hs

index 2db054e88bcc2c831d3d0439c911daf318af8b41..e4097ed4e9cb395091f1362602d6325e3c268512 100644 (file)
@@ -178,11 +178,11 @@ catSymLinkTarget sha = fromInternalGitPath . L.toStrict <$> get
 catKeyFile :: RawFilePath -> Annex (Maybe Key)
 catKeyFile f = ifM (Annex.getState Annex.daemon)
        ( catKeyFileHEAD f
-       , catKey $ Git.Ref.fileRef f
+       , catKey =<< liftIO (Git.Ref.fileRef f)
        )
 
 catKeyFileHEAD :: RawFilePath -> Annex (Maybe Key)
-catKeyFileHEAD f = catKey $ Git.Ref.fileFromRef Git.Ref.headRef f
+catKeyFileHEAD f = catKey =<< liftIO (Git.Ref.fileFromRef Git.Ref.headRef f)
 
 {- Look in the original branch from whence an adjusted branch is based
  - to find the file. But only when the adjustment hides some files. -}
@@ -194,5 +194,6 @@ catObjectMetaDataHidden = hiddenCat catObjectMetaData
 
 hiddenCat :: (Ref -> Annex (Maybe a)) -> RawFilePath -> CurrBranch -> Annex (Maybe a)
 hiddenCat a f (Just origbranch, Just adj)
-       | adjustmentHidesFiles adj = a (Git.Ref.fileFromRef origbranch f)
+       | adjustmentHidesFiles adj = 
+               a =<< liftIO (Git.Ref.fileFromRef origbranch f)
 hiddenCat _ _ _ = return Nothing
index f2ca2067343ede4d845726e2227cbffa3298ad99..5a5772156a9eee44636563ca3383ebd6f064cf1f 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,9 @@ git-annex (8.20210429) UNRELEASED; urgency=medium
   * smudge: Fix a case where an unlocked annexed file that annex.largefiles
     does not match could get its unchanged content checked into git,
     due to git running the smudge filter unecessarily.
+  * Fix behavior of several commands, including reinject, addurl, and rmurl
+    when given an absolute path to an unlocked file, or a relative path
+    that leaves and re-enters the repository.
 
  -- Joey Hess <id@joeyh.name>  Mon, 03 May 2021 10:33:10 -0400
 
index 3c73022fc0d4cb890a0be48ebffaadb63582adc5..cbecd055f695a0bbbadaedb66531ed6bad993fa5 100644 (file)
@@ -103,7 +103,7 @@ clean file = do
                                getMoveRaceRecovery k file
                                liftIO $ L.hPut stdout b
                Nothing -> do
-                       let fileref = Git.Ref.fileRef file
+                       fileref <- liftIO $ Git.Ref.fileRef file
                        indexmeta <- catObjectMetaData fileref
                        oldkey <- case indexmeta of
                                Just (_, sz, _) -> catKey' fileref sz
index fac7ea41f8b84b6eb06ab2ee0795d372247a65ea..6929a8ee91e301a117054055fe542182b3a28a6a 100644 (file)
@@ -64,12 +64,17 @@ branchRef = underBase "refs/heads"
 
 {- A Ref that can be used to refer to a file in the repository, as staged
  - in the index.
- -
- - Prefixing the file with ./ makes this work even if in a subdirectory
- - of a repo.
  -}
-fileRef :: RawFilePath -> Ref
-fileRef f = Ref $ ":./" <> toInternalGitPath f
+fileRef :: RawFilePath -> IO Ref
+fileRef f = do
+       -- The filename could be absolute, or contain eg "../repo/file",
+       -- neither of which work in a ref, so convert it to a minimal
+       -- relative path.
+       f' <- relPathCwdToFile f
+       -- Prefixing the file with ./ makes this work even when in a
+       -- subdirectory of a repo. Eg, ./foo in directory bar refers
+       -- to bar/foo, not to foo in the top of the repository.
+       return $ Ref $ ":./" <> toInternalGitPath f'
 
 {- A Ref that can be used to refer to a file in a particular branch. -}
 branchFileRef :: Branch -> RawFilePath -> Ref
@@ -81,8 +86,10 @@ dateRef r (RefDate d) = Ref $ fromRef' r <> "@" <> encodeBS' d
 
 {- A Ref that can be used to refer to a file in the repository as it
  - appears in a given Ref. -}
-fileFromRef :: Ref -> RawFilePath -> Ref
-fileFromRef r f = let (Ref fr) = fileRef f in Ref (fromRef' r <> fr)
+fileFromRef :: Ref -> RawFilePath -> IO Ref
+fileFromRef r f = do
+       (Ref fr) <- fileRef f
+       return (Ref (fromRef' r <> fr))
 
 {- Checks if a ref exists. Note that it must be fully qualified,
  - eg refs/heads/master rather than master. -}
index 951d0aa1b8f050d50dca6d907e4d0fb2d7d2bd28..857dd5e3200e0dd415920c8c122292525cd64b6b 100644 (file)
@@ -1,6 +1,6 @@
 {- absolute and relative path manipulation
  -
- - Copyright 2010-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2021 Joey Hess <id@joeyh.name>
  -
  - License: BSD-2-clause
  -}
@@ -19,6 +19,7 @@ module Utility.Path.AbsRel (
 ) where
 
 import System.FilePath.ByteString
+import qualified Data.ByteString as B
 #ifdef mingw32_HOST_OS
 import System.Directory (getCurrentDirectory)
 #else
@@ -72,13 +73,17 @@ absPath file
  -    relPathCwdToFile "../bar/baz" == "baz"
  -}
 relPathCwdToFile :: RawFilePath -> IO RawFilePath
-relPathCwdToFile f = do
+relPathCwdToFile f
+       -- Optimisation: Avoid doing any IO when the path is relative
+       -- and does not contain any ".." component.
+       | isRelative f && not (".." `B.isInfixOf` f) = return f
+       | otherwise = do
 #ifdef mingw32_HOST_OS
-       c <- toRawFilePath <$> getCurrentDirectory
+               c <- toRawFilePath <$> getCurrentDirectory
 #else
-       c <- getWorkingDirectory
+               c <- getWorkingDirectory
 #endif
-       relPathDirToFile c f
+               relPathDirToFile c f
 
 {- Constructs a minimal relative path from a directory to a file. -}
 relPathDirToFile :: RawFilePath -> RawFilePath -> IO RawFilePath