remove most remnants of direct mode
A few remain, as needed for upgrades, and for accessing objects from remotes that are direct mode repos that have not been converted yet.
This commit is contained in:
parent
adb89ee71b
commit
689d1fcc92
37 changed files with 193 additions and 799 deletions
|
@ -13,7 +13,6 @@ module Annex.AutoMerge
|
||||||
|
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
import qualified Annex.Queue
|
import qualified Annex.Queue
|
||||||
import Annex.Direct
|
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
|
@ -50,9 +49,7 @@ autoMergeFrom branch currbranch mergeconfig canresolvemerge commitmode = do
|
||||||
Nothing -> go Nothing
|
Nothing -> go Nothing
|
||||||
Just b -> go =<< inRepo (Git.Ref.sha b)
|
Just b -> go =<< inRepo (Git.Ref.sha b)
|
||||||
where
|
where
|
||||||
go old = ifM isDirect
|
go old = do
|
||||||
( mergeDirect currbranch old branch resolvemerge mergeconfig commitmode
|
|
||||||
, do
|
|
||||||
r <- inRepo (Git.Merge.merge branch mergeconfig commitmode)
|
r <- inRepo (Git.Merge.merge branch mergeconfig commitmode)
|
||||||
<||> (resolvemerge <&&> commitResolvedMerge commitmode)
|
<||> (resolvemerge <&&> commitResolvedMerge commitmode)
|
||||||
-- Merging can cause new associated files to appear
|
-- Merging can cause new associated files to appear
|
||||||
|
@ -61,7 +58,6 @@ autoMergeFrom branch currbranch mergeconfig canresolvemerge commitmode = do
|
||||||
-- close the database if it was open.
|
-- close the database if it was open.
|
||||||
Database.Keys.closeDb
|
Database.Keys.closeDb
|
||||||
return r
|
return r
|
||||||
)
|
|
||||||
where
|
where
|
||||||
resolvemerge = ifM canresolvemerge
|
resolvemerge = ifM canresolvemerge
|
||||||
( resolveMerge old branch False
|
( resolveMerge old branch False
|
||||||
|
@ -88,14 +84,10 @@ autoMergeFrom branch currbranch mergeconfig canresolvemerge commitmode = do
|
||||||
- the other as a directory or non-annexed file. The annexed file
|
- the other as a directory or non-annexed file. The annexed file
|
||||||
- is renamed to resolve the merge, and the other object is preserved as-is.
|
- is renamed to resolve the merge, and the other object is preserved as-is.
|
||||||
-
|
-
|
||||||
- In indirect mode, the merge is resolved in the work tree and files
|
- The merge is resolved in the work tree and files
|
||||||
- staged, to clean up from a conflicted merge that was run in the work
|
- staged, to clean up from a conflicted merge that was run in the work
|
||||||
- tree.
|
- tree.
|
||||||
-
|
-
|
||||||
- In direct mode, the work tree is not touched here; files are staged to
|
|
||||||
- the index, and written to the gitAnnexMergeDir, for later handling by
|
|
||||||
- the direct mode merge code.
|
|
||||||
-
|
|
||||||
- This is complicated by needing to support merges run in an overlay
|
- This is complicated by needing to support merges run in an overlay
|
||||||
- work tree, in which case the CWD won't be within the work tree.
|
- work tree, in which case the CWD won't be within the work tree.
|
||||||
- In this mode, there is no need to update the work tree at all,
|
- In this mode, there is no need to update the work tree at all,
|
||||||
|
@ -126,7 +118,7 @@ resolveMerge us them inoverlay = do
|
||||||
let merged = not (null mergedfs')
|
let merged = not (null mergedfs')
|
||||||
void $ liftIO cleanup
|
void $ liftIO cleanup
|
||||||
|
|
||||||
unlessM (pure inoverlay <||> isDirect) $ do
|
unless inoverlay $ do
|
||||||
(deleted, cleanup2) <- inRepo (LsFiles.deleted [top])
|
(deleted, cleanup2) <- inRepo (LsFiles.deleted [top])
|
||||||
unless (null deleted) $
|
unless (null deleted) $
|
||||||
Annex.Queue.addCommand "rm"
|
Annex.Queue.addCommand "rm"
|
||||||
|
@ -136,7 +128,7 @@ resolveMerge us them inoverlay = do
|
||||||
|
|
||||||
when merged $ do
|
when merged $ do
|
||||||
Annex.Queue.flush
|
Annex.Queue.flush
|
||||||
unlessM (pure inoverlay <||> isDirect) $ do
|
unless inoverlay $ do
|
||||||
unstagedmap <- inodeMap $ inRepo $ LsFiles.notInRepo False [top]
|
unstagedmap <- inodeMap $ inRepo $ LsFiles.notInRepo False [top]
|
||||||
cleanConflictCruft mergedks' mergedfs' unstagedmap
|
cleanConflictCruft mergedks' mergedfs' unstagedmap
|
||||||
showLongNote "Merge conflict was automatically resolved; you may want to examine the result."
|
showLongNote "Merge conflict was automatically resolved; you may want to examine the result."
|
||||||
|
@ -230,12 +222,7 @@ resolveMerge' unstagedmap (Just us) them inoverlay u = do
|
||||||
Database.Keys.addAssociatedFile key
|
Database.Keys.addAssociatedFile key
|
||||||
=<< inRepo (toTopFilePath dest)
|
=<< inRepo (toTopFilePath dest)
|
||||||
|
|
||||||
withworktree f a = ifM isDirect
|
withworktree f a = a f
|
||||||
( do
|
|
||||||
d <- fromRepo gitAnnexMergeDir
|
|
||||||
a (d </> f)
|
|
||||||
, a f
|
|
||||||
)
|
|
||||||
|
|
||||||
{- Stage a graft of a directory or file from a branch
|
{- Stage a graft of a directory or file from a branch
|
||||||
- and update the work tree. -}
|
- and update the work tree. -}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
{- git-annex file content managing for direct mode
|
{- git-annex file content managing for old direct mode repositories
|
||||||
-
|
|
||||||
- This is deprecated, and will be removed when direct mode gets removed
|
|
||||||
- from git-annex.
|
|
||||||
-
|
-
|
||||||
- Copyright 2012-2014 Joey Hess <id@joeyh.name>
|
- Copyright 2012-2014 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
|
@ -10,22 +7,15 @@
|
||||||
|
|
||||||
module Annex.Content.Direct (
|
module Annex.Content.Direct (
|
||||||
associatedFiles,
|
associatedFiles,
|
||||||
associatedFilesRelative,
|
|
||||||
removeAssociatedFile,
|
removeAssociatedFile,
|
||||||
removeAssociatedFileUnchecked,
|
|
||||||
removeAssociatedFiles,
|
removeAssociatedFiles,
|
||||||
addAssociatedFile,
|
addAssociatedFile,
|
||||||
goodContent,
|
goodContent,
|
||||||
recordedInodeCache,
|
recordedInodeCache,
|
||||||
updateInodeCache,
|
updateInodeCache,
|
||||||
addInodeCache,
|
addInodeCache,
|
||||||
writeInodeCache,
|
|
||||||
compareInodeCaches,
|
|
||||||
sameInodeCache,
|
sameInodeCache,
|
||||||
elemInodeCaches,
|
|
||||||
sameFileStatus,
|
|
||||||
removeInodeCache,
|
removeInodeCache,
|
||||||
toInodeCache,
|
|
||||||
addContentWhenNotPresent,
|
addContentWhenNotPresent,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|
336
Annex/Direct.hs
336
Annex/Direct.hs
|
@ -1,26 +1,25 @@
|
||||||
{- git-annex direct mode
|
{- git-annex direct mode
|
||||||
-
|
-
|
||||||
- This is deprecated, and will be removed when direct mode gets removed
|
- This only contains some remnants needed to convert away from direct mode.
|
||||||
- from git-annex.
|
|
||||||
-
|
-
|
||||||
- Copyright 2012-2014 Joey Hess <id@joeyh.name>
|
- Copyright 2012-2014 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Annex.Direct where
|
module Annex.Direct (
|
||||||
|
switchHEADBack,
|
||||||
|
stageDirect,
|
||||||
|
setIndirect,
|
||||||
|
) where
|
||||||
|
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import qualified Git.LsFiles
|
import qualified Git.LsFiles
|
||||||
import qualified Git.Merge
|
|
||||||
import qualified Git.DiffTree as DiffTree
|
|
||||||
import qualified Git.Config
|
import qualified Git.Config
|
||||||
import qualified Git.Ref
|
import qualified Git.Ref
|
||||||
import qualified Git.Branch
|
import qualified Git.Branch
|
||||||
import Git.Sha
|
|
||||||
import Git.FilePath
|
|
||||||
import Git.Types
|
import Git.Types
|
||||||
import Config
|
import Config
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
|
@ -28,17 +27,9 @@ import qualified Annex.Queue
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
import Backend
|
import Backend
|
||||||
import Types.KeySource
|
import Types.KeySource
|
||||||
import Annex.Content
|
|
||||||
import Annex.Content.Direct
|
import Annex.Content.Direct
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Utility.InodeCache
|
import Utility.InodeCache
|
||||||
import Utility.CopyFile
|
|
||||||
import Annex.Perms
|
|
||||||
import Annex.ReplaceFile
|
|
||||||
import Annex.VariantFile
|
|
||||||
import Git.Index
|
|
||||||
import Annex.GitOverlay
|
|
||||||
import Annex.LockFile
|
|
||||||
import Annex.InodeSentinal
|
import Annex.InodeSentinal
|
||||||
import Utility.Metered
|
import Utility.Metered
|
||||||
|
|
||||||
|
@ -102,25 +93,6 @@ stageDirect = do
|
||||||
|
|
||||||
deletegit file = Annex.Queue.addCommand "rm" [Param "-qf"] [file]
|
deletegit file = Annex.Queue.addCommand "rm" [Param "-qf"] [file]
|
||||||
|
|
||||||
{- Run before a commit to update direct mode bookeeping to reflect the
|
|
||||||
- staged changes being committed. -}
|
|
||||||
preCommitDirect :: Annex Bool
|
|
||||||
preCommitDirect = do
|
|
||||||
(diffs, clean) <- inRepo $ DiffTree.diffIndex Git.Ref.headRef
|
|
||||||
makeabs <- flip fromTopFilePath <$> gitRepo
|
|
||||||
forM_ diffs (go makeabs)
|
|
||||||
liftIO clean
|
|
||||||
where
|
|
||||||
go makeabs diff = do
|
|
||||||
withkey (DiffTree.srcsha diff) (DiffTree.srcmode diff) removeAssociatedFile
|
|
||||||
withkey (DiffTree.dstsha diff) (DiffTree.dstmode diff) addAssociatedFile
|
|
||||||
where
|
|
||||||
withkey sha _mode a = when (sha /= nullSha) $
|
|
||||||
catKey sha >>= \case
|
|
||||||
Nothing -> noop
|
|
||||||
Just key -> void $ a key $
|
|
||||||
makeabs $ DiffTree.file diff
|
|
||||||
|
|
||||||
{- Adds a file to the annex in direct mode. Can fail, if the file is
|
{- Adds a file to the annex in direct mode. Can fail, if the file is
|
||||||
- modified or deleted while it's being added. -}
|
- modified or deleted while it's being added. -}
|
||||||
addDirect :: FilePath -> InodeCache -> Annex Bool
|
addDirect :: FilePath -> InodeCache -> Annex Bool
|
||||||
|
@ -150,275 +122,14 @@ addDirect file cache = do
|
||||||
return False
|
return False
|
||||||
)
|
)
|
||||||
|
|
||||||
{- In direct mode, git merge would usually refuse to do anything, since it
|
setIndirect :: Annex ()
|
||||||
- sees present direct mode files as type changed files.
|
setIndirect = do
|
||||||
-
|
|
||||||
- So, to handle a merge, it's run with the work tree set to a temp
|
|
||||||
- directory, and the merge is staged into a copy of the index.
|
|
||||||
- Then the work tree is updated to reflect the merge, and
|
|
||||||
- finally, the merge is committed and the real index updated.
|
|
||||||
-
|
|
||||||
- A lock file is used to avoid races with any other caller of mergeDirect.
|
|
||||||
-
|
|
||||||
- To avoid other git processes from making changes to the index while our
|
|
||||||
- merge is in progress, the index lock file is used as the temp index
|
|
||||||
- file. This is the same as what git does when updating the index
|
|
||||||
- normally.
|
|
||||||
-}
|
|
||||||
mergeDirect :: Maybe Git.Ref -> Maybe Git.Ref -> Git.Branch -> Annex Bool -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Annex Bool
|
|
||||||
mergeDirect startbranch oldref branch resolvemerge mergeconfig commitmode = exclusively $ do
|
|
||||||
reali <- liftIO . absPath =<< fromRepo indexFile
|
|
||||||
tmpi <- liftIO . absPath =<< fromRepo (indexFileLock . indexFile)
|
|
||||||
liftIO $ whenM (doesFileExist reali) $
|
|
||||||
copyFile reali tmpi
|
|
||||||
|
|
||||||
d <- fromRepo gitAnnexMergeDir
|
|
||||||
liftIO $ do
|
|
||||||
whenM (doesDirectoryExist d) $
|
|
||||||
removeDirectoryRecursive d
|
|
||||||
createDirectoryIfMissing True d
|
|
||||||
|
|
||||||
withIndexFile tmpi $ do
|
|
||||||
merged <- stageMerge d branch mergeconfig commitmode
|
|
||||||
ok <- if merged
|
|
||||||
then return True
|
|
||||||
else resolvemerge
|
|
||||||
if ok
|
|
||||||
then do
|
|
||||||
mergeDirectCleanup d (fromMaybe Git.Sha.emptyTree oldref)
|
|
||||||
mergeDirectCommit merged startbranch branch commitmode
|
|
||||||
liftIO $ whenM (doesFileExist tmpi) $
|
|
||||||
rename tmpi reali
|
|
||||||
else do
|
|
||||||
liftIO $ nukeFile tmpi
|
|
||||||
liftIO $ removeDirectoryRecursive d
|
|
||||||
return ok
|
|
||||||
where
|
|
||||||
exclusively = withExclusiveLock gitAnnexMergeLock
|
|
||||||
|
|
||||||
{- Stage a merge into the index, avoiding changing HEAD or the current
|
|
||||||
- branch. -}
|
|
||||||
stageMerge :: FilePath -> Git.Branch -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Annex Bool
|
|
||||||
stageMerge d branch mergeconfig commitmode = do
|
|
||||||
-- XXX A bug in git makes stageMerge unsafe to use if the git repo
|
|
||||||
-- is configured with core.symlinks=false
|
|
||||||
-- Using merge is not ideal though, since it will
|
|
||||||
-- update the current branch immediately, before the work tree
|
|
||||||
-- has been updated, which would leave things in an inconsistent
|
|
||||||
-- state if mergeDirectCleanup is interrupted.
|
|
||||||
-- <http://marc.info/?l=git&m=140262402204212&w=2>
|
|
||||||
merger <- ifM (coreSymlinks <$> Annex.getGitConfig)
|
|
||||||
( return $ \ref -> Git.Merge.stageMerge ref mergeconfig
|
|
||||||
, return $ \ref -> Git.Merge.merge ref mergeconfig commitmode
|
|
||||||
)
|
|
||||||
inRepo $ \g -> do
|
|
||||||
wd <- liftIO $ absPath d
|
|
||||||
gd <- liftIO $ absPath $ Git.localGitDir g
|
|
||||||
merger branch $
|
|
||||||
g { location = Local { gitdir = gd, worktree = Just (addTrailingPathSeparator wd) } }
|
|
||||||
|
|
||||||
{- Commits after a direct mode merge is complete, and after the work
|
|
||||||
- tree has been updated by mergeDirectCleanup.
|
|
||||||
-}
|
|
||||||
mergeDirectCommit :: Bool -> Maybe Git.Ref -> Git.Branch -> Git.Branch.CommitMode -> Annex ()
|
|
||||||
mergeDirectCommit allowff old branch commitmode = do
|
|
||||||
void preCommitDirect
|
|
||||||
d <- fromRepo Git.localGitDir
|
|
||||||
let merge_head = d </> "MERGE_HEAD"
|
|
||||||
let merge_msg = d </> "MERGE_MSG"
|
|
||||||
let merge_mode = d </> "MERGE_MODE"
|
|
||||||
ifM (pure allowff <&&> canff)
|
|
||||||
( inRepo $ Git.Branch.update "merge" Git.Ref.headRef branch -- fast forward
|
|
||||||
, do
|
|
||||||
msg <- liftIO $
|
|
||||||
catchDefaultIO ("merge " ++ fromRef branch) $
|
|
||||||
readFile merge_msg
|
|
||||||
void $ inRepo $ Git.Branch.commit commitmode False msg
|
|
||||||
Git.Ref.headRef [Git.Ref.headRef, branch]
|
|
||||||
)
|
|
||||||
liftIO $ mapM_ nukeFile [merge_head, merge_msg, merge_mode]
|
|
||||||
where
|
|
||||||
canff = maybe (return False) (\o -> inRepo $ Git.Branch.fastForwardable o branch) old
|
|
||||||
|
|
||||||
mergeDirectCleanup :: FilePath -> Git.Ref -> Annex ()
|
|
||||||
mergeDirectCleanup d oldref = updateWorkTree d oldref False
|
|
||||||
|
|
||||||
{- Updates the direct mode work tree to reflect the changes staged in the
|
|
||||||
- index by a git command, that was run in a temporary work tree.
|
|
||||||
-
|
|
||||||
- Uses diff-index to compare the staged changes with provided ref
|
|
||||||
- which should be the tree before the merge, and applies those
|
|
||||||
- changes to the work tree.
|
|
||||||
-
|
|
||||||
- There are really only two types of changes: An old item can be deleted,
|
|
||||||
- or a new item added. Two passes are made, first deleting and then
|
|
||||||
- adding. This is to handle cases where eg, a file is deleted and a
|
|
||||||
- directory is added. (The diff-tree output may list these in the opposite
|
|
||||||
- order, but we cannot add the directory until the file with the
|
|
||||||
- same name is removed.)
|
|
||||||
-}
|
|
||||||
updateWorkTree :: FilePath -> Git.Ref -> Bool -> Annex ()
|
|
||||||
updateWorkTree d oldref force = do
|
|
||||||
(items, cleanup) <- inRepo $ DiffTree.diffIndex oldref
|
|
||||||
makeabs <- flip fromTopFilePath <$> gitRepo
|
|
||||||
let fsitems = zip (map (makeabs . DiffTree.file) items) items
|
|
||||||
forM_ fsitems $
|
|
||||||
go makeabs DiffTree.srcsha moveout moveout_raw
|
|
||||||
forM_ fsitems $
|
|
||||||
go makeabs DiffTree.dstsha movein movein_raw
|
|
||||||
void $ liftIO cleanup
|
|
||||||
where
|
|
||||||
go makeabs getsha a araw (f, item)
|
|
||||||
| getsha item == nullSha = noop
|
|
||||||
| otherwise = void $
|
|
||||||
tryNonAsync . maybe (araw item makeabs f) (\k -> void $ a item makeabs k f)
|
|
||||||
=<< catKey (getsha item)
|
|
||||||
|
|
||||||
moveout _ _ = removeDirect
|
|
||||||
|
|
||||||
{- Files deleted by the merge are removed from the work tree.
|
|
||||||
- Empty work tree directories are removed, per git behavior. -}
|
|
||||||
moveout_raw _ _ f = liftIO $ do
|
|
||||||
nukeFile f
|
|
||||||
void $ tryIO $ removeDirectory $ parentDir f
|
|
||||||
|
|
||||||
{- If the file is already present, with the right content for the
|
|
||||||
- key, it's left alone.
|
|
||||||
-
|
|
||||||
- If the file is already present, and does not exist in the
|
|
||||||
- oldref, preserve this local file.
|
|
||||||
-
|
|
||||||
- Otherwise, create the symlink and then if possible, replace it
|
|
||||||
- with the content. -}
|
|
||||||
movein item makeabs k f = unlessM (goodContent k f) $ do
|
|
||||||
unless force $ preserveUnannexed item makeabs f oldref
|
|
||||||
l <- calcRepo $ gitAnnexLink f k
|
|
||||||
replaceFile f $ makeAnnexLink l
|
|
||||||
toDirect k f
|
|
||||||
|
|
||||||
{- Any new, modified, or renamed files were written to the temp
|
|
||||||
- directory by the merge, and are moved to the real work tree. -}
|
|
||||||
movein_raw item makeabs f = do
|
|
||||||
unless force $ preserveUnannexed item makeabs f oldref
|
|
||||||
liftIO $ do
|
|
||||||
createDirectoryIfMissing True $ parentDir f
|
|
||||||
void $ tryIO $ rename (d </> getTopFilePath (DiffTree.file item)) f
|
|
||||||
|
|
||||||
{- If the file that's being moved in is already present in the work
|
|
||||||
- tree, but did not exist in the oldref, preserve this
|
|
||||||
- local, unannexed file (or directory), as "variant-local".
|
|
||||||
-
|
|
||||||
- It's also possible that the file that's being moved in
|
|
||||||
- is in a directory that collides with an exsting, non-annexed
|
|
||||||
- file (not a directory), which should be preserved.
|
|
||||||
-}
|
|
||||||
preserveUnannexed :: DiffTree.DiffTreeItem -> (TopFilePath -> FilePath) -> FilePath -> Ref -> Annex ()
|
|
||||||
preserveUnannexed item makeabs absf oldref = do
|
|
||||||
whenM (liftIO (collidingitem absf) <&&> unannexed absf) $
|
|
||||||
liftIO $ findnewname absf 0
|
|
||||||
checkdirs (DiffTree.file item)
|
|
||||||
where
|
|
||||||
checkdirs from = case upFrom (getTopFilePath from) of
|
|
||||||
Nothing -> noop
|
|
||||||
Just p -> do
|
|
||||||
let d = asTopFilePath p
|
|
||||||
let absd = makeabs d
|
|
||||||
whenM (liftIO (colliding_nondir absd) <&&> unannexed absd) $
|
|
||||||
liftIO $ findnewname absd 0
|
|
||||||
checkdirs d
|
|
||||||
|
|
||||||
collidingitem f = isJust
|
|
||||||
<$> catchMaybeIO (getSymbolicLinkStatus f)
|
|
||||||
colliding_nondir f = maybe False (not . isDirectory)
|
|
||||||
<$> catchMaybeIO (getSymbolicLinkStatus f)
|
|
||||||
|
|
||||||
unannexed f = (isNothing <$> isAnnexLink f)
|
|
||||||
<&&> (isNothing <$> catFileDetails oldref f)
|
|
||||||
|
|
||||||
findnewname :: FilePath -> Int -> IO ()
|
|
||||||
findnewname f n = do
|
|
||||||
let localf = mkVariant f
|
|
||||||
("local" ++ if n > 0 then show n else "")
|
|
||||||
ifM (collidingitem localf)
|
|
||||||
( findnewname f (n+1)
|
|
||||||
, rename f localf
|
|
||||||
`catchIO` const (findnewname f (n+1))
|
|
||||||
)
|
|
||||||
|
|
||||||
{- If possible, converts a symlink in the working tree into a direct
|
|
||||||
- mode file. If the content is not available, leaves the symlink
|
|
||||||
- unchanged. -}
|
|
||||||
toDirect :: Key -> FilePath -> Annex ()
|
|
||||||
toDirect k f = fromMaybe noop =<< toDirectGen k f
|
|
||||||
|
|
||||||
toDirectGen :: Key -> FilePath -> Annex (Maybe (Annex ()))
|
|
||||||
toDirectGen k f = do
|
|
||||||
loc <- calcRepo $ gitAnnexLocation k
|
|
||||||
ifM (liftIO $ doesFileExist loc)
|
|
||||||
( return $ Just $ fromindirect loc
|
|
||||||
, do
|
|
||||||
{- Copy content from another direct file. -}
|
|
||||||
absf <- liftIO $ absPath f
|
|
||||||
dlocs <- filterM (goodContent k) =<<
|
|
||||||
filterM (\l -> isNothing <$> getAnnexLinkTarget l) =<<
|
|
||||||
(filter (/= absf) <$> addAssociatedFile k f)
|
|
||||||
case dlocs of
|
|
||||||
[] -> return Nothing
|
|
||||||
(dloc:_) -> return $ Just $ fromdirect dloc
|
|
||||||
)
|
|
||||||
where
|
|
||||||
fromindirect loc = do
|
|
||||||
{- Move content from annex to direct file. -}
|
|
||||||
updateInodeCache k loc
|
|
||||||
void $ addAssociatedFile k f
|
|
||||||
modifyContent loc $ do
|
|
||||||
thawContent loc
|
|
||||||
liftIO (replaceFileFrom loc f)
|
|
||||||
`catchIO` (\_ -> freezeContent loc)
|
|
||||||
fromdirect loc = do
|
|
||||||
replaceFile f $
|
|
||||||
liftIO . void . copyFileExternal CopyAllMetaData loc
|
|
||||||
updateInodeCache k f
|
|
||||||
|
|
||||||
{- Removes a direct mode file, while retaining its content in the annex
|
|
||||||
- (unless its content has already been changed). -}
|
|
||||||
removeDirect :: Key -> FilePath -> Annex ()
|
|
||||||
removeDirect k f = do
|
|
||||||
void $ removeAssociatedFileUnchecked k f
|
|
||||||
unlessM (inAnnex k) $
|
|
||||||
-- If moveAnnex rejects the content of the key,
|
|
||||||
-- treat that the same as its content having changed.
|
|
||||||
ifM (goodContent k f)
|
|
||||||
( unlessM (moveAnnex k f) $
|
|
||||||
logStatus k InfoMissing
|
|
||||||
, logStatus k InfoMissing
|
|
||||||
)
|
|
||||||
liftIO $ do
|
|
||||||
nukeFile f
|
|
||||||
void $ tryIO $ removeDirectory $ parentDir f
|
|
||||||
|
|
||||||
{- Called when a direct mode file has been changed. Its old content may be
|
|
||||||
- lost. -}
|
|
||||||
changedDirect :: Key -> FilePath -> Annex ()
|
|
||||||
changedDirect oldk f = do
|
|
||||||
locs <- removeAssociatedFile oldk f
|
|
||||||
whenM (pure (null locs) <&&> not <$> inAnnex oldk) $
|
|
||||||
logStatus oldk InfoMissing
|
|
||||||
|
|
||||||
{- Git config settings to enable/disable direct mode. -}
|
|
||||||
setDirect :: Bool -> Annex ()
|
|
||||||
setDirect wantdirect = do
|
|
||||||
if wantdirect
|
|
||||||
then do
|
|
||||||
switchHEAD
|
|
||||||
setbare
|
|
||||||
else do
|
|
||||||
setbare
|
setbare
|
||||||
switchHEADBack
|
switchHEADBack
|
||||||
setConfig (annexConfig "direct") val
|
setConfig (annexConfig "direct") val
|
||||||
Annex.changeGitConfig $ \c -> c { annexDirect = wantdirect }
|
Annex.changeGitConfig $ \c -> c { annexDirect = False }
|
||||||
where
|
where
|
||||||
val = Git.Config.boolConfig wantdirect
|
val = Git.Config.boolConfig False
|
||||||
coreworktree = ConfigKey "core.worktree"
|
coreworktree = ConfigKey "core.worktree"
|
||||||
indirectworktree = ConfigKey "core.indirect-worktree"
|
indirectworktree = ConfigKey "core.indirect-worktree"
|
||||||
setbare = do
|
setbare = do
|
||||||
|
@ -426,9 +137,7 @@ setDirect wantdirect = do
|
||||||
-- core.bare; git does not allow both to be set, so
|
-- core.bare; git does not allow both to be set, so
|
||||||
-- unset it when enabling direct mode, caching in
|
-- unset it when enabling direct mode, caching in
|
||||||
-- core.indirect-worktree
|
-- core.indirect-worktree
|
||||||
if wantdirect
|
moveconfig indirectworktree coreworktree
|
||||||
then moveconfig coreworktree indirectworktree
|
|
||||||
else moveconfig indirectworktree coreworktree
|
|
||||||
setConfig (ConfigKey Git.Config.coreBare) val
|
setConfig (ConfigKey Git.Config.coreBare) val
|
||||||
moveconfig src dest = getConfigMaybe src >>= \case
|
moveconfig src dest = getConfigMaybe src >>= \case
|
||||||
Nothing -> noop
|
Nothing -> noop
|
||||||
|
@ -437,20 +146,6 @@ setDirect wantdirect = do
|
||||||
setConfig dest wt
|
setConfig dest wt
|
||||||
reloadConfig
|
reloadConfig
|
||||||
|
|
||||||
{- Since direct mode sets core.bare=true, incoming pushes could change
|
|
||||||
- the currently checked out branch. To avoid this problem, HEAD
|
|
||||||
- is changed to a internal ref that nothing is going to push to.
|
|
||||||
-
|
|
||||||
- For refs/heads/master, use refs/heads/annex/direct/master;
|
|
||||||
- this way things that show HEAD (eg shell prompts) will
|
|
||||||
- hopefully show just "master". -}
|
|
||||||
directBranch :: Ref -> Ref
|
|
||||||
directBranch orighead = case splitc '/' $ fromRef orighead of
|
|
||||||
("refs":"heads":"annex":"direct":_) -> orighead
|
|
||||||
("refs":"heads":rest) ->
|
|
||||||
Ref $ "refs/heads/annex/direct/" ++ intercalate "/" rest
|
|
||||||
_ -> Ref $ "refs/heads/" ++ fromRef (Git.Ref.base orighead)
|
|
||||||
|
|
||||||
{- Converts a directBranch back to the original branch.
|
{- Converts a directBranch back to the original branch.
|
||||||
-
|
-
|
||||||
- Any other ref is left unchanged.
|
- Any other ref is left unchanged.
|
||||||
|
@ -461,15 +156,6 @@ fromDirectBranch directhead = case splitc '/' $ fromRef directhead of
|
||||||
Ref $ "refs/heads/" ++ intercalate "/" rest
|
Ref $ "refs/heads/" ++ intercalate "/" rest
|
||||||
_ -> directhead
|
_ -> directhead
|
||||||
|
|
||||||
switchHEAD :: Annex ()
|
|
||||||
switchHEAD = maybe noop switch =<< inRepo Git.Branch.currentUnsafe
|
|
||||||
where
|
|
||||||
switch orighead = do
|
|
||||||
let newhead = directBranch orighead
|
|
||||||
maybe noop (inRepo . Git.Branch.update "entering direct mode" newhead)
|
|
||||||
=<< inRepo (Git.Ref.sha orighead)
|
|
||||||
inRepo $ Git.Branch.checkout newhead
|
|
||||||
|
|
||||||
switchHEADBack :: Annex ()
|
switchHEADBack :: Annex ()
|
||||||
switchHEADBack = maybe noop switch =<< inRepo Git.Branch.currentUnsafe
|
switchHEADBack = maybe noop switch =<< inRepo Git.Branch.currentUnsafe
|
||||||
where
|
where
|
||||||
|
|
|
@ -17,7 +17,6 @@ import qualified Command.Drop
|
||||||
import Command
|
import Command
|
||||||
import Annex.Wanted
|
import Annex.Wanted
|
||||||
import Config
|
import Config
|
||||||
import Annex.Content.Direct
|
|
||||||
import qualified Database.Keys
|
import qualified Database.Keys
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
|
|
||||||
|
@ -44,20 +43,13 @@ type Reason = String
|
||||||
- A VerifiedCopy can be provided as an optimisation when eg, a key
|
- A VerifiedCopy can be provided as an optimisation when eg, a key
|
||||||
- has just been uploaded to a remote.
|
- has just been uploaded to a remote.
|
||||||
-
|
-
|
||||||
- In direct mode, all associated files are checked, and only if all
|
|
||||||
- of them are unwanted are they dropped.
|
|
||||||
-
|
|
||||||
- The runner is used to run CommandStart sequentially, it's typically
|
- The runner is used to run CommandStart sequentially, it's typically
|
||||||
- callCommandAction.
|
- callCommandAction.
|
||||||
-}
|
-}
|
||||||
handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile -> [VerifiedCopy] -> (CommandStart -> CommandCleanup) -> Annex ()
|
handleDropsFrom :: [UUID] -> [Remote] -> Reason -> Bool -> Key -> AssociatedFile -> [VerifiedCopy] -> (CommandStart -> CommandCleanup) -> Annex ()
|
||||||
handleDropsFrom locs rs reason fromhere key afile preverified runner = do
|
handleDropsFrom locs rs reason fromhere key afile preverified runner = do
|
||||||
l <- ifM isDirect
|
|
||||||
( associatedFilesRelative key
|
|
||||||
, do
|
|
||||||
g <- Annex.gitRepo
|
g <- Annex.gitRepo
|
||||||
map (`fromTopFilePath` g) <$> Database.Keys.getAssociatedFiles key
|
l <- map (`fromTopFilePath` g) <$> Database.Keys.getAssociatedFiles key
|
||||||
)
|
|
||||||
let fs = case afile of
|
let fs = case afile of
|
||||||
AssociatedFile (Just f) -> nub (f : l)
|
AssociatedFile (Just f) -> nub (f : l)
|
||||||
AssociatedFile Nothing -> l
|
AssociatedFile Nothing -> l
|
||||||
|
|
|
@ -13,7 +13,6 @@ module Annex.Ingest (
|
||||||
ingestAdd',
|
ingestAdd',
|
||||||
ingest,
|
ingest,
|
||||||
ingest',
|
ingest',
|
||||||
finishIngestDirect,
|
|
||||||
finishIngestUnlocked,
|
finishIngestUnlocked,
|
||||||
cleanOldKeys,
|
cleanOldKeys,
|
||||||
addLink,
|
addLink,
|
||||||
|
@ -28,7 +27,6 @@ import Annex.Common
|
||||||
import Types.KeySource
|
import Types.KeySource
|
||||||
import Backend
|
import Backend
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import Annex.Content.Direct
|
|
||||||
import Annex.Perms
|
import Annex.Perms
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Annex.MetaData
|
import Annex.MetaData
|
||||||
|
@ -137,14 +135,9 @@ ingestAdd' meterupdate ld@(Just (LockedDown cfg source)) mk = do
|
||||||
let f = keyFilename source
|
let f = keyFilename source
|
||||||
if lockingFile cfg
|
if lockingFile cfg
|
||||||
then addLink f k mic
|
then addLink f k mic
|
||||||
else ifM isDirect
|
else do
|
||||||
( do
|
|
||||||
l <- calcRepo $ gitAnnexLink f k
|
|
||||||
stageSymlink f =<< hashSymlink l
|
|
||||||
, do
|
|
||||||
mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus (contentLocation source)
|
mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus (contentLocation source)
|
||||||
stagePointerFile f mode =<< hashPointerFile k
|
stagePointerFile f mode =<< hashPointerFile k
|
||||||
)
|
|
||||||
return (Just k)
|
return (Just k)
|
||||||
|
|
||||||
{- Ingests a locked down file into the annex. Does not update the working
|
{- Ingests a locked down file into the annex. Does not update the working
|
||||||
|
@ -170,10 +163,7 @@ ingest' preferredbackend meterupdate (Just (LockedDown cfg source)) mk restage =
|
||||||
where
|
where
|
||||||
go (Just key) mcache (Just s)
|
go (Just key) mcache (Just s)
|
||||||
| lockingFile cfg = golocked key mcache s
|
| lockingFile cfg = golocked key mcache s
|
||||||
| otherwise = ifM isDirect
|
| otherwise = gounlocked key mcache s
|
||||||
( godirect key mcache s
|
|
||||||
, gounlocked key mcache s
|
|
||||||
)
|
|
||||||
go _ _ _ = failure "failed to generate a key"
|
go _ _ _ = failure "failed to generate a key"
|
||||||
|
|
||||||
golocked key mcache s =
|
golocked key mcache s =
|
||||||
|
@ -197,12 +187,6 @@ ingest' preferredbackend meterupdate (Just (LockedDown cfg source)) mk restage =
|
||||||
success key (Just cache) s
|
success key (Just cache) s
|
||||||
gounlocked _ _ _ = failure "failed statting file"
|
gounlocked _ _ _ = failure "failed statting file"
|
||||||
|
|
||||||
godirect key (Just cache) s = do
|
|
||||||
addInodeCache key cache
|
|
||||||
finishIngestDirect key source
|
|
||||||
success key (Just cache) s
|
|
||||||
godirect _ _ _ = failure "failed statting file"
|
|
||||||
|
|
||||||
success k mcache s = do
|
success k mcache s = do
|
||||||
genMetaData k (keyFilename source) s
|
genMetaData k (keyFilename source) s
|
||||||
return (Just k, mcache)
|
return (Just k, mcache)
|
||||||
|
@ -212,16 +196,6 @@ ingest' preferredbackend meterupdate (Just (LockedDown cfg source)) mk restage =
|
||||||
cleanCruft source
|
cleanCruft source
|
||||||
return (Nothing, Nothing)
|
return (Nothing, Nothing)
|
||||||
|
|
||||||
finishIngestDirect :: Key -> KeySource -> Annex ()
|
|
||||||
finishIngestDirect key source = do
|
|
||||||
void $ addAssociatedFile key $ keyFilename source
|
|
||||||
cleanCruft source
|
|
||||||
|
|
||||||
{- Copy to any other locations using the same key. -}
|
|
||||||
otherfs <- filter (/= keyFilename source) <$> associatedFiles key
|
|
||||||
forM_ otherfs $
|
|
||||||
addContentWhenNotPresent key (keyFilename source)
|
|
||||||
|
|
||||||
finishIngestUnlocked :: Key -> KeySource -> Annex ()
|
finishIngestUnlocked :: Key -> KeySource -> Annex ()
|
||||||
finishIngestUnlocked key source = do
|
finishIngestUnlocked key source = do
|
||||||
cleanCruft source
|
cleanCruft source
|
||||||
|
@ -333,7 +307,7 @@ forceParams = ifM (Annex.getState Annex.force)
|
||||||
- Also, when in an adjusted unlocked branch, always add files unlocked.
|
- Also, when in an adjusted unlocked branch, always add files unlocked.
|
||||||
-}
|
-}
|
||||||
addUnlocked :: Annex Bool
|
addUnlocked :: Annex Bool
|
||||||
addUnlocked = isDirect <||>
|
addUnlocked =
|
||||||
(versionSupportsUnlockedPointers <&&>
|
(versionSupportsUnlockedPointers <&&>
|
||||||
((not . coreSymlinks <$> Annex.getGitConfig) <||>
|
((not . coreSymlinks <$> Annex.getGitConfig) <||>
|
||||||
(annexAddUnlocked <$> Annex.getGitConfig) <||>
|
(annexAddUnlocked <$> Annex.getGitConfig) <||>
|
||||||
|
@ -352,7 +326,7 @@ addUnlocked = isDirect <||>
|
||||||
- When the content of the key is not accepted into the annex, returns False.
|
- When the content of the key is not accepted into the annex, returns False.
|
||||||
-}
|
-}
|
||||||
addAnnexedFile :: FilePath -> Key -> Maybe FilePath -> Annex Bool
|
addAnnexedFile :: FilePath -> Key -> Maybe FilePath -> Annex Bool
|
||||||
addAnnexedFile file key mtmp = ifM (addUnlocked <&&> not <$> isDirect)
|
addAnnexedFile file key mtmp = ifM addUnlocked
|
||||||
( do
|
( do
|
||||||
mode <- maybe
|
mode <- maybe
|
||||||
(pure Nothing)
|
(pure Nothing)
|
||||||
|
@ -371,15 +345,8 @@ addAnnexedFile file key mtmp = ifM (addUnlocked <&&> not <$> isDirect)
|
||||||
)
|
)
|
||||||
, do
|
, do
|
||||||
addLink file key Nothing
|
addLink file key Nothing
|
||||||
whenM isDirect $ do
|
|
||||||
void $ addAssociatedFile key file
|
|
||||||
case mtmp of
|
case mtmp of
|
||||||
Just tmp -> do
|
Just tmp -> moveAnnex key tmp
|
||||||
{- For moveAnnex to work in direct mode, the
|
|
||||||
- symlink must already exist, so flush the queue. -}
|
|
||||||
whenM isDirect $
|
|
||||||
Annex.Queue.flush
|
|
||||||
moveAnnex key tmp
|
|
||||||
Nothing -> return True
|
Nothing -> return True
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
|
@ -35,7 +35,7 @@ import Annex.WorkTree
|
||||||
import Config
|
import Config
|
||||||
import Config.Files
|
import Config.Files
|
||||||
import Config.Smudge
|
import Config.Smudge
|
||||||
import Annex.Direct
|
import qualified Annex.Direct as Direct
|
||||||
import qualified Annex.AdjustedBranch as AdjustedBranch
|
import qualified Annex.AdjustedBranch as AdjustedBranch
|
||||||
import Annex.Environment
|
import Annex.Environment
|
||||||
import Annex.Hook
|
import Annex.Hook
|
||||||
|
@ -126,7 +126,7 @@ initialize' mversion = checkCanInitialize $ do
|
||||||
-- Handle case where this repo was cloned from a
|
-- Handle case where this repo was cloned from a
|
||||||
-- direct mode repo
|
-- direct mode repo
|
||||||
, unlessM isBareRepo
|
, unlessM isBareRepo
|
||||||
switchHEADBack
|
Direct.switchHEADBack
|
||||||
)
|
)
|
||||||
propigateSecureHashesOnly
|
propigateSecureHashesOnly
|
||||||
createInodeSentinalFile False
|
createInodeSentinalFile False
|
||||||
|
|
|
@ -9,17 +9,15 @@ module Annex.UpdateInstead where
|
||||||
|
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
import Config
|
|
||||||
import Annex.Version
|
import Annex.Version
|
||||||
import Annex.AdjustedBranch
|
import Annex.AdjustedBranch
|
||||||
import Git.Branch
|
import Git.Branch
|
||||||
import Git.ConfigTypes
|
import Git.ConfigTypes
|
||||||
|
|
||||||
{- receive.denyCurrentBranch=updateInstead does not work in direct mode
|
{- receive.denyCurrentBranch=updateInstead does not work
|
||||||
- repositories or when an adjusted branch is checked out, so must be
|
- when an adjusted branch is checked out, so must be emulated. -}
|
||||||
- emulated. -}
|
|
||||||
needUpdateInsteadEmulation :: Annex Bool
|
needUpdateInsteadEmulation :: Annex Bool
|
||||||
needUpdateInsteadEmulation = updateinsteadset <&&> (isDirect <||> isadjusted)
|
needUpdateInsteadEmulation = updateinsteadset <&&> isadjusted
|
||||||
where
|
where
|
||||||
updateinsteadset = (== UpdateInstead) . receiveDenyCurrentBranch
|
updateinsteadset = (== UpdateInstead) . receiveDenyCurrentBranch
|
||||||
<$> Annex.getGitConfig
|
<$> Annex.getGitConfig
|
||||||
|
|
|
@ -36,7 +36,6 @@ import Annex.Version
|
||||||
import Annex.CurrentBranch
|
import Annex.CurrentBranch
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import Utility.InodeCache
|
import Utility.InodeCache
|
||||||
import Annex.Content.Direct
|
|
||||||
import qualified Database.Keys
|
import qualified Database.Keys
|
||||||
import qualified Command.Sync
|
import qualified Command.Sync
|
||||||
import qualified Git.Branch
|
import qualified Git.Branch
|
||||||
|
@ -245,7 +244,7 @@ commitStaged msg = do
|
||||||
- access the file after closing it. -}
|
- access the file after closing it. -}
|
||||||
delayaddDefault :: Annex (Maybe Seconds)
|
delayaddDefault :: Annex (Maybe Seconds)
|
||||||
#ifdef darwin_HOST_OS
|
#ifdef darwin_HOST_OS
|
||||||
delayaddDefault = ifM (isDirect <||> versionSupportsUnlockedPointers)
|
delayaddDefault = ifM versionSupportsUnlockedPointers
|
||||||
( return Nothing
|
( return Nothing
|
||||||
, return $ Just $ Seconds 1
|
, return $ Just $ Seconds 1
|
||||||
)
|
)
|
||||||
|
@ -275,14 +274,13 @@ delayaddDefault = return Nothing
|
||||||
handleAdds :: FilePath -> Bool -> Maybe Seconds -> [Change] -> Assistant [Change]
|
handleAdds :: FilePath -> Bool -> Maybe Seconds -> [Change] -> Assistant [Change]
|
||||||
handleAdds lockdowndir havelsof delayadd cs = returnWhen (null incomplete) $ do
|
handleAdds lockdowndir havelsof delayadd cs = returnWhen (null incomplete) $ do
|
||||||
let (pending, inprocess) = partition isPendingAddChange incomplete
|
let (pending, inprocess) = partition isPendingAddChange incomplete
|
||||||
direct <- liftAnnex isDirect
|
|
||||||
unlocked <- liftAnnex versionSupportsUnlockedPointers
|
unlocked <- liftAnnex versionSupportsUnlockedPointers
|
||||||
let lockingfiles = not (unlocked || direct)
|
let lockingfiles = not unlocked
|
||||||
let lockdownconfig = LockDownConfig
|
let lockdownconfig = LockDownConfig
|
||||||
{ lockingFile = lockingfiles
|
{ lockingFile = lockingfiles
|
||||||
, hardlinkFileTmpDir = Just lockdowndir
|
, hardlinkFileTmpDir = Just lockdowndir
|
||||||
}
|
}
|
||||||
(pending', cleanup) <- if unlocked || direct
|
(pending', cleanup) <- if unlocked
|
||||||
then return (pending, noop)
|
then return (pending, noop)
|
||||||
else findnew pending
|
else findnew pending
|
||||||
(postponed, toadd) <- partitionEithers
|
(postponed, toadd) <- partitionEithers
|
||||||
|
@ -296,9 +294,9 @@ handleAdds lockdowndir havelsof delayadd cs = returnWhen (null incomplete) $ do
|
||||||
added <- addaction toadd $
|
added <- addaction toadd $
|
||||||
catMaybes <$>
|
catMaybes <$>
|
||||||
if not lockingfiles
|
if not lockingfiles
|
||||||
then addunlocked direct toadd
|
then addunlocked toadd
|
||||||
else forM toadd (add lockdownconfig)
|
else forM toadd (add lockdownconfig)
|
||||||
if DirWatcher.eventsCoalesce || null added || unlocked || direct
|
if DirWatcher.eventsCoalesce || null added || unlocked
|
||||||
then return $ added ++ otherchanges
|
then return $ added ++ otherchanges
|
||||||
else do
|
else do
|
||||||
r <- handleAdds lockdowndir havelsof delayadd =<< getChanges
|
r <- handleAdds lockdowndir havelsof delayadd =<< getChanges
|
||||||
|
@ -341,10 +339,10 @@ handleAdds lockdowndir havelsof delayadd cs = returnWhen (null incomplete) $ do
|
||||||
- same InodeCache as the new file. If so, we can just update
|
- same InodeCache as the new file. If so, we can just update
|
||||||
- bookkeeping, and stage the file in git.
|
- bookkeeping, and stage the file in git.
|
||||||
-}
|
-}
|
||||||
addunlocked :: Bool -> [Change] -> Assistant [Maybe Change]
|
addunlocked :: [Change] -> Assistant [Maybe Change]
|
||||||
addunlocked isdirect toadd = do
|
addunlocked toadd = do
|
||||||
ct <- liftAnnex compareInodeCachesWith
|
ct <- liftAnnex compareInodeCachesWith
|
||||||
m <- liftAnnex $ removedKeysMap isdirect ct cs
|
m <- liftAnnex $ removedKeysMap ct cs
|
||||||
delta <- liftAnnex getTSDelta
|
delta <- liftAnnex getTSDelta
|
||||||
let cfg = LockDownConfig
|
let cfg = LockDownConfig
|
||||||
{ lockingFile = False
|
{ lockingFile = False
|
||||||
|
@ -359,26 +357,22 @@ handleAdds lockdowndir havelsof delayadd cs = returnWhen (null incomplete) $ do
|
||||||
Just cache ->
|
Just cache ->
|
||||||
case M.lookup (inodeCacheToKey ct cache) m of
|
case M.lookup (inodeCacheToKey ct cache) m of
|
||||||
Nothing -> add cfg c
|
Nothing -> add cfg c
|
||||||
Just k -> fastadd isdirect c k
|
Just k -> fastadd c k
|
||||||
|
|
||||||
fastadd :: Bool -> Change -> Key -> Assistant (Maybe Change)
|
fastadd :: Change -> Key -> Assistant (Maybe Change)
|
||||||
fastadd isdirect change key = do
|
fastadd change key = do
|
||||||
let source = keySource $ lockedDown change
|
let source = keySource $ lockedDown change
|
||||||
liftAnnex $ if isdirect
|
liftAnnex $ finishIngestUnlocked key source
|
||||||
then finishIngestDirect key source
|
|
||||||
else finishIngestUnlocked key source
|
|
||||||
done change Nothing (keyFilename source) key
|
done change Nothing (keyFilename source) key
|
||||||
|
|
||||||
removedKeysMap :: Bool -> InodeComparisonType -> [Change] -> Annex (M.Map InodeCacheKey Key)
|
removedKeysMap :: InodeComparisonType -> [Change] -> Annex (M.Map InodeCacheKey Key)
|
||||||
removedKeysMap isdirect ct l = do
|
removedKeysMap ct l = do
|
||||||
mks <- forM (filter isRmChange l) $ \c ->
|
mks <- forM (filter isRmChange l) $ \c ->
|
||||||
catKeyFile $ changeFile c
|
catKeyFile $ changeFile c
|
||||||
M.fromList . concat <$> mapM mkpairs (catMaybes mks)
|
M.fromList . concat <$> mapM mkpairs (catMaybes mks)
|
||||||
where
|
where
|
||||||
mkpairs k = map (\c -> (inodeCacheToKey ct c, k)) <$>
|
mkpairs k = map (\c -> (inodeCacheToKey ct c, k)) <$>
|
||||||
if isdirect
|
Database.Keys.getInodeCaches k
|
||||||
then recordedInodeCache k
|
|
||||||
else Database.Keys.getInodeCaches k
|
|
||||||
|
|
||||||
failedingest change = do
|
failedingest change = do
|
||||||
refill [retryChange change]
|
refill [retryChange change]
|
||||||
|
@ -392,11 +386,8 @@ handleAdds lockdowndir havelsof delayadd cs = returnWhen (null incomplete) $ do
|
||||||
mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus file
|
mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus file
|
||||||
stagePointerFile file mode =<< hashPointerFile key
|
stagePointerFile file mode =<< hashPointerFile key
|
||||||
, do
|
, do
|
||||||
link <- ifM isDirect
|
link <- makeLink file key mcache
|
||||||
( calcRepo $ gitAnnexLink file key
|
when DirWatcher.eventsCoalesce $
|
||||||
, makeLink file key mcache
|
|
||||||
)
|
|
||||||
whenM (pure DirWatcher.eventsCoalesce <||> isDirect) $
|
|
||||||
stageSymlink file =<< hashSymlink link
|
stageSymlink file =<< hashSymlink link
|
||||||
)
|
)
|
||||||
showEndOk
|
showEndOk
|
||||||
|
|
|
@ -32,7 +32,6 @@ import Utility.ThreadScheduler
|
||||||
import qualified Assistant.Threads.Watcher as Watcher
|
import qualified Assistant.Threads.Watcher as Watcher
|
||||||
import Utility.Batch
|
import Utility.Batch
|
||||||
import Utility.NotificationBroadcaster
|
import Utility.NotificationBroadcaster
|
||||||
import Config
|
|
||||||
import Utility.HumanTime
|
import Utility.HumanTime
|
||||||
import Utility.Tense
|
import Utility.Tense
|
||||||
import Git.Repair
|
import Git.Repair
|
||||||
|
@ -200,8 +199,7 @@ dailyCheck urlrenderer = do
|
||||||
liftAnnex $ warning msg
|
liftAnnex $ warning msg
|
||||||
void $ addAlert $ sanityCheckFixAlert msg
|
void $ addAlert $ sanityCheckFixAlert msg
|
||||||
addsymlink file s = do
|
addsymlink file s = do
|
||||||
isdirect <- liftAnnex isDirect
|
Watcher.runHandler Watcher.onAddSymlink file s
|
||||||
Watcher.runHandler (Watcher.onAddSymlink isdirect) file s
|
|
||||||
insanity $ "found unstaged symlink: " ++ file
|
insanity $ "found unstaged symlink: " ++ file
|
||||||
|
|
||||||
hourlyCheck :: Assistant ()
|
hourlyCheck :: Assistant ()
|
||||||
|
|
|
@ -23,14 +23,13 @@ import Assistant.Types.Changes
|
||||||
import Assistant.Alert
|
import Assistant.Alert
|
||||||
import Utility.DirWatcher
|
import Utility.DirWatcher
|
||||||
import Utility.DirWatcher.Types
|
import Utility.DirWatcher.Types
|
||||||
|
import Utility.InodeCache
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import qualified Annex.Queue
|
import qualified Annex.Queue
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import qualified Git.UpdateIndex
|
import qualified Git.UpdateIndex
|
||||||
import qualified Git.LsFiles as LsFiles
|
import qualified Git.LsFiles as LsFiles
|
||||||
import Annex.WorkTree
|
import Annex.WorkTree
|
||||||
import Annex.Direct
|
|
||||||
import Annex.Content.Direct
|
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
import Annex.CheckIgnore
|
import Annex.CheckIgnore
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
|
@ -41,7 +40,6 @@ import Annex.Version
|
||||||
import Annex.InodeSentinal
|
import Annex.InodeSentinal
|
||||||
import Git.Types
|
import Git.Types
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
import Config
|
|
||||||
import Config.GitConfig
|
import Config.GitConfig
|
||||||
import Utility.ThreadScheduler
|
import Utility.ThreadScheduler
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
|
@ -92,16 +90,13 @@ runWatcher :: Assistant ()
|
||||||
runWatcher = do
|
runWatcher = do
|
||||||
startup <- asIO1 startupScan
|
startup <- asIO1 startupScan
|
||||||
matcher <- liftAnnex largeFilesMatcher
|
matcher <- liftAnnex largeFilesMatcher
|
||||||
direct <- liftAnnex isDirect
|
|
||||||
unlocked <- liftAnnex versionSupportsUnlockedPointers
|
unlocked <- liftAnnex versionSupportsUnlockedPointers
|
||||||
symlinkssupported <- liftAnnex $ coreSymlinks <$> Annex.getGitConfig
|
symlinkssupported <- liftAnnex $ coreSymlinks <$> Annex.getGitConfig
|
||||||
addhook <- hook $ if unlocked
|
addhook <- hook $ if unlocked
|
||||||
then onAddUnlocked symlinkssupported matcher
|
then onAddUnlocked symlinkssupported matcher
|
||||||
else if direct
|
|
||||||
then onAddDirect symlinkssupported matcher
|
|
||||||
else onAdd matcher
|
else onAdd matcher
|
||||||
delhook <- hook onDel
|
delhook <- hook onDel
|
||||||
addsymlinkhook <- hook $ onAddSymlink direct
|
addsymlinkhook <- hook onAddSymlink
|
||||||
deldirhook <- hook onDelDir
|
deldirhook <- hook onDelDir
|
||||||
errhook <- hook onErr
|
errhook <- hook onErr
|
||||||
let hooks = mkWatchHooks
|
let hooks = mkWatchHooks
|
||||||
|
@ -224,7 +219,7 @@ onAddUnlocked :: Bool -> GetFileMatcher -> Handler
|
||||||
onAddUnlocked symlinkssupported matcher f fs = do
|
onAddUnlocked symlinkssupported matcher f fs = do
|
||||||
mk <- liftIO $ isPointerFile f
|
mk <- liftIO $ isPointerFile f
|
||||||
case mk of
|
case mk of
|
||||||
Nothing -> onAddUnlocked' False contentchanged addassociatedfile addlink samefilestatus symlinkssupported matcher f fs
|
Nothing -> onAddUnlocked' contentchanged addassociatedfile addlink samefilestatus symlinkssupported matcher f fs
|
||||||
Just k -> addlink f k
|
Just k -> addlink f k
|
||||||
where
|
where
|
||||||
addassociatedfile key file =
|
addassociatedfile key file =
|
||||||
|
@ -247,27 +242,15 @@ onAddUnlocked symlinkssupported matcher f fs = do
|
||||||
liftAnnex $ stagePointerFile file mode =<< hashPointerFile key
|
liftAnnex $ stagePointerFile file mode =<< hashPointerFile key
|
||||||
madeChange file $ LinkChange (Just key)
|
madeChange file $ LinkChange (Just key)
|
||||||
|
|
||||||
{- In direct mode, add events are received for both new files, and
|
|
||||||
- modified existing files.
|
|
||||||
-}
|
|
||||||
onAddDirect :: Bool -> GetFileMatcher -> Handler
|
|
||||||
onAddDirect = onAddUnlocked' True changedDirect addassociatedfile addlink sameFileStatus
|
|
||||||
where
|
|
||||||
addassociatedfile key file = void $ addAssociatedFile key file
|
|
||||||
addlink file key = do
|
|
||||||
link <- liftAnnex $ calcRepo $ gitAnnexLink file key
|
|
||||||
addLink file link (Just key)
|
|
||||||
|
|
||||||
onAddUnlocked'
|
onAddUnlocked'
|
||||||
:: Bool
|
:: (Key -> FilePath -> Annex ())
|
||||||
-> (Key -> FilePath -> Annex ())
|
|
||||||
-> (Key -> FilePath -> Annex ())
|
-> (Key -> FilePath -> Annex ())
|
||||||
-> (FilePath -> Key -> Assistant (Maybe Change))
|
-> (FilePath -> Key -> Assistant (Maybe Change))
|
||||||
-> (Key -> FilePath -> FileStatus -> Annex Bool)
|
-> (Key -> FilePath -> FileStatus -> Annex Bool)
|
||||||
-> Bool
|
-> Bool
|
||||||
-> GetFileMatcher
|
-> GetFileMatcher
|
||||||
-> Handler
|
-> Handler
|
||||||
onAddUnlocked' isdirect contentchanged addassociatedfile addlink samefilestatus symlinkssupported matcher file fs = do
|
onAddUnlocked' contentchanged addassociatedfile addlink samefilestatus symlinkssupported matcher file fs = do
|
||||||
v <- liftAnnex $ catKeyFile file
|
v <- liftAnnex $ catKeyFile file
|
||||||
case (v, fs) of
|
case (v, fs) of
|
||||||
(Just key, Just filestatus) ->
|
(Just key, Just filestatus) ->
|
||||||
|
@ -306,29 +289,26 @@ onAddUnlocked' isdirect contentchanged addassociatedfile addlink samefilestatus
|
||||||
Nothing -> noop
|
Nothing -> noop
|
||||||
Just key -> liftAnnex $
|
Just key -> liftAnnex $
|
||||||
addassociatedfile key file
|
addassociatedfile key file
|
||||||
onAddSymlink' (Just $ fromRawFilePath lt) mk isdirect file fs
|
onAddSymlink' (Just $ fromRawFilePath lt) mk file fs
|
||||||
|
|
||||||
{- A symlink might be an arbitrary symlink, which is just added.
|
{- A symlink might be an arbitrary symlink, which is just added.
|
||||||
- Or, if it is a git-annex symlink, ensure it points to the content
|
- Or, if it is a git-annex symlink, ensure it points to the content
|
||||||
- before adding it.
|
- before adding it.
|
||||||
-}
|
-}
|
||||||
onAddSymlink :: Bool -> Handler
|
onAddSymlink :: Handler
|
||||||
onAddSymlink isdirect file filestatus = unlessIgnored file $ do
|
onAddSymlink file filestatus = unlessIgnored file $ do
|
||||||
linktarget <- liftIO (catchMaybeIO $ readSymbolicLink file)
|
linktarget <- liftIO (catchMaybeIO $ readSymbolicLink file)
|
||||||
kv <- liftAnnex (lookupFile file)
|
kv <- liftAnnex (lookupFile file)
|
||||||
onAddSymlink' linktarget kv isdirect file filestatus
|
onAddSymlink' linktarget kv file filestatus
|
||||||
|
|
||||||
onAddSymlink' :: Maybe String -> Maybe Key -> Bool -> Handler
|
onAddSymlink' :: Maybe String -> Maybe Key -> Handler
|
||||||
onAddSymlink' linktarget mk isdirect file filestatus = go mk
|
onAddSymlink' linktarget mk file filestatus = go mk
|
||||||
where
|
where
|
||||||
go (Just key) = do
|
go (Just key) = do
|
||||||
when isdirect $
|
|
||||||
liftAnnex $ void $ addAssociatedFile key file
|
|
||||||
link <- liftAnnex $ calcRepo $ gitAnnexLink file key
|
link <- liftAnnex $ calcRepo $ gitAnnexLink file key
|
||||||
if linktarget == Just link
|
if linktarget == Just link
|
||||||
then ensurestaged (Just link) =<< getDaemonStatus
|
then ensurestaged (Just link) =<< getDaemonStatus
|
||||||
else do
|
else do
|
||||||
unless isdirect $
|
|
||||||
liftAnnex $ replaceFile file $
|
liftAnnex $ replaceFile file $
|
||||||
makeAnnexLink link
|
makeAnnexLink link
|
||||||
addLink file link (Just key)
|
addLink file link (Just key)
|
||||||
|
@ -376,11 +356,8 @@ onDel file _ = do
|
||||||
onDel' :: FilePath -> Annex ()
|
onDel' :: FilePath -> Annex ()
|
||||||
onDel' file = do
|
onDel' file = do
|
||||||
topfile <- inRepo (toTopFilePath file)
|
topfile <- inRepo (toTopFilePath file)
|
||||||
ifM versionSupportsUnlockedPointers
|
whenM versionSupportsUnlockedPointers $
|
||||||
( withkey $ flip Database.Keys.removeAssociatedFile topfile
|
withkey $ flip Database.Keys.removeAssociatedFile topfile
|
||||||
, whenM isDirect $
|
|
||||||
withkey $ \key -> void $ removeAssociatedFile key file
|
|
||||||
)
|
|
||||||
Annex.Queue.addUpdateIndex =<<
|
Annex.Queue.addUpdateIndex =<<
|
||||||
inRepo (Git.UpdateIndex.unstageFile file)
|
inRepo (Git.UpdateIndex.unstageFile file)
|
||||||
where
|
where
|
||||||
|
|
|
@ -24,7 +24,6 @@ import CmdLine.Batch as ReExported
|
||||||
import Options.Applicative as ReExported hiding (command)
|
import Options.Applicative as ReExported hiding (command)
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import Annex.Init
|
import Annex.Init
|
||||||
import Config
|
|
||||||
import Utility.Daemon
|
import Utility.Daemon
|
||||||
import Types.Transfer
|
import Types.Transfer
|
||||||
import Types.ActionItem
|
import Types.ActionItem
|
||||||
|
@ -120,10 +119,6 @@ commonChecks = [repoExists]
|
||||||
repoExists :: CommandCheck
|
repoExists :: CommandCheck
|
||||||
repoExists = CommandCheck 0 ensureInitialized
|
repoExists = CommandCheck 0 ensureInitialized
|
||||||
|
|
||||||
notDirect :: Command -> Command
|
|
||||||
notDirect = addCheck $ whenM isDirect $
|
|
||||||
giveup "You cannot run this command in a direct mode repository."
|
|
||||||
|
|
||||||
notBareRepo :: Command -> Command
|
notBareRepo :: Command -> Command
|
||||||
notBareRepo = addCheck $ whenM (fromRepo Git.repoIsLocalBare) $
|
notBareRepo = addCheck $ whenM (fromRepo Git.repoIsLocalBare) $
|
||||||
giveup "You cannot run this command in a bare repository."
|
giveup "You cannot run this command in a bare repository."
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Command
|
||||||
import Annex.Ingest
|
import Annex.Ingest
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import Annex.Content.Direct
|
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import qualified Annex.Queue
|
import qualified Annex.Queue
|
||||||
import qualified Database.Keys
|
import qualified Database.Keys
|
||||||
|
@ -73,8 +72,7 @@ seek o = startConcurrency commandStages $ do
|
||||||
go withFilesMaybeModified
|
go withFilesMaybeModified
|
||||||
ifM versionSupportsUnlockedPointers
|
ifM versionSupportsUnlockedPointers
|
||||||
( go withUnmodifiedUnlockedPointers
|
( go withUnmodifiedUnlockedPointers
|
||||||
, unlessM isDirect $
|
, go withFilesOldUnlocked
|
||||||
go withFilesOldUnlocked
|
|
||||||
)
|
)
|
||||||
|
|
||||||
{- Pass file off to git-add. -}
|
{- Pass file off to git-add. -}
|
||||||
|
@ -116,14 +114,8 @@ start file = do
|
||||||
( liftIO (catchMaybeIO $ getSymbolicLinkStatus file) >>= \case
|
( liftIO (catchMaybeIO $ getSymbolicLinkStatus file) >>= \case
|
||||||
Just s | isSymbolicLink s -> fixuplink key
|
Just s | isSymbolicLink s -> fixuplink key
|
||||||
_ -> add
|
_ -> add
|
||||||
, ifM isDirect
|
|
||||||
( liftIO (catchMaybeIO $ getSymbolicLinkStatus file) >>= \case
|
|
||||||
Just s | isSymbolicLink s -> fixuplink key
|
|
||||||
_ -> ifM (goodContent key file)
|
|
||||||
( stop , add )
|
|
||||||
, fixuplink key
|
, fixuplink key
|
||||||
)
|
)
|
||||||
)
|
|
||||||
fixuplink key = starting "add" (ActionItemWorkTreeFile file) $ do
|
fixuplink key = starting "add" (ActionItemWorkTreeFile file) $ do
|
||||||
-- the annexed symlink is present but not yet added to git
|
-- the annexed symlink is present but not yet added to git
|
||||||
liftIO $ removeFile file
|
liftIO $ removeFile file
|
||||||
|
|
|
@ -13,8 +13,7 @@ import Annex.Ingest
|
||||||
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
|
import Command.Unused (withUnusedMaps, UnusedMaps(..), startUnused)
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $
|
cmd = command "addunused" SectionMaintenance
|
||||||
command "addunused" SectionMaintenance
|
|
||||||
"add back unused files"
|
"add back unused files"
|
||||||
(paramRepeating paramNumRange) (withParams seek)
|
(paramRepeating paramNumRange) (withParams seek)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import Command
|
||||||
import Annex.AdjustedBranch
|
import Annex.AdjustedBranch
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notBareRepo $ notDirect $ noDaemonRunning $
|
cmd = notBareRepo $ noDaemonRunning $
|
||||||
command "adjust" SectionSetup "enter adjusted branch"
|
command "adjust" SectionSetup "enter adjusted branch"
|
||||||
paramNothing (seek <$$> optParser)
|
paramNothing (seek <$$> optParser)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ import System.Posix.Files
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $ noCommit $ withGlobalOptions [annexedMatchingOptions] $
|
cmd = noCommit $ withGlobalOptions [annexedMatchingOptions] $
|
||||||
command "fix" SectionMaintenance
|
command "fix" SectionMaintenance
|
||||||
"fix up links to annexed content"
|
"fix up links to annexed content"
|
||||||
paramPaths (withParams seek)
|
paramPaths (withParams seek)
|
||||||
|
|
|
@ -19,7 +19,7 @@ import qualified Backend.URL
|
||||||
import Network.URI
|
import Network.URI
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $ notBareRepo $ withGlobalOptions [jsonOptions] $
|
cmd = notBareRepo $ withGlobalOptions [jsonOptions] $
|
||||||
command "fromkey" SectionPlumbing "adds a file using a specific key"
|
command "fromkey" SectionPlumbing "adds a file using a specific key"
|
||||||
(paramRepeating (paramPair paramKey paramPath))
|
(paramRepeating (paramPair paramKey paramPath))
|
||||||
(seek <$$> optParser)
|
(seek <$$> optParser)
|
||||||
|
|
|
@ -15,8 +15,6 @@ import qualified Remote
|
||||||
import qualified Types.Backend
|
import qualified Types.Backend
|
||||||
import qualified Backend
|
import qualified Backend
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import qualified Annex.Content.Direct as Direct
|
|
||||||
import Annex.Direct
|
|
||||||
import Annex.Perms
|
import Annex.Perms
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
|
@ -28,7 +26,6 @@ import Annex.NumCopies
|
||||||
import Annex.UUID
|
import Annex.UUID
|
||||||
import Annex.ReplaceFile
|
import Annex.ReplaceFile
|
||||||
import Utility.DataUnits
|
import Utility.DataUnits
|
||||||
import Config
|
|
||||||
import Utility.HumanTime
|
import Utility.HumanTime
|
||||||
import Utility.CopyFile
|
import Utility.CopyFile
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
|
@ -225,16 +222,15 @@ fixLink key file = do
|
||||||
- in this repository only. -}
|
- in this repository only. -}
|
||||||
verifyLocationLog :: Key -> KeyStatus -> ActionItem -> Annex Bool
|
verifyLocationLog :: Key -> KeyStatus -> ActionItem -> Annex Bool
|
||||||
verifyLocationLog key keystatus ai = do
|
verifyLocationLog key keystatus ai = do
|
||||||
direct <- isDirect
|
|
||||||
obj <- calcRepo $ gitAnnexLocation key
|
obj <- calcRepo $ gitAnnexLocation key
|
||||||
present <- if not direct && isKeyUnlockedThin keystatus
|
present <- if isKeyUnlockedThin keystatus
|
||||||
then liftIO (doesFileExist obj)
|
then liftIO (doesFileExist obj)
|
||||||
else inAnnex key
|
else inAnnex key
|
||||||
u <- getUUID
|
u <- getUUID
|
||||||
|
|
||||||
{- Since we're checking that a key's object file is present, throw
|
{- Since we're checking that a key's object file is present, throw
|
||||||
- in a permission fixup here too. -}
|
- in a permission fixup here too. -}
|
||||||
when (present && not direct) $ do
|
when present $ do
|
||||||
void $ tryIO $ case keystatus of
|
void $ tryIO $ case keystatus of
|
||||||
KeyUnlockedThin -> thawContent obj
|
KeyUnlockedThin -> thawContent obj
|
||||||
KeyLockedThin -> thawContent obj
|
KeyLockedThin -> thawContent obj
|
||||||
|
@ -252,9 +248,7 @@ verifyLocationLog key keystatus ai = do
|
||||||
whenM (annexSecureHashesOnly <$> Annex.getGitConfig) $
|
whenM (annexSecureHashesOnly <$> Annex.getGitConfig) $
|
||||||
warning $ "** Despite annex.securehashesonly being set, " ++ obj ++ " has content present in the annex using an insecure " ++ decodeBS (formatKeyVariety (keyVariety key)) ++ " key"
|
warning $ "** Despite annex.securehashesonly being set, " ++ obj ++ " has content present in the annex using an insecure " ++ decodeBS (formatKeyVariety (keyVariety key)) ++ " key"
|
||||||
|
|
||||||
{- In direct mode, modified files will show up as not present,
|
if not present
|
||||||
- but that is expected and not something to do anything about. -}
|
|
||||||
if direct && not present
|
|
||||||
then return True
|
then return True
|
||||||
else verifyLocationLog' key ai present u (logChange key u)
|
else verifyLocationLog' key ai present u (logChange key u)
|
||||||
|
|
||||||
|
@ -319,36 +313,17 @@ verifyRequiredContent _ _ = return True
|
||||||
{- Verifies the associated file records. -}
|
{- Verifies the associated file records. -}
|
||||||
verifyAssociatedFiles :: Key -> KeyStatus -> FilePath -> Annex Bool
|
verifyAssociatedFiles :: Key -> KeyStatus -> FilePath -> Annex Bool
|
||||||
verifyAssociatedFiles key keystatus file = do
|
verifyAssociatedFiles key keystatus file = do
|
||||||
ifM isDirect (godirect, goindirect)
|
when (isKeyUnlockedThin keystatus) $ do
|
||||||
return True
|
|
||||||
where
|
|
||||||
godirect = do
|
|
||||||
fs <- Direct.addAssociatedFile key file
|
|
||||||
forM_ fs $ \f ->
|
|
||||||
unlessM (liftIO $ doesFileExist f) $
|
|
||||||
void $ Direct.removeAssociatedFile key f
|
|
||||||
goindirect = when (isKeyUnlockedThin keystatus) $ do
|
|
||||||
f <- inRepo $ toTopFilePath file
|
f <- inRepo $ toTopFilePath file
|
||||||
afs <- Database.Keys.getAssociatedFiles key
|
afs <- Database.Keys.getAssociatedFiles key
|
||||||
unless (getTopFilePath f `elem` map getTopFilePath afs) $
|
unless (getTopFilePath f `elem` map getTopFilePath afs) $
|
||||||
Database.Keys.addAssociatedFile key f
|
Database.Keys.addAssociatedFile key f
|
||||||
|
return True
|
||||||
|
|
||||||
verifyWorkTree :: Key -> FilePath -> Annex Bool
|
verifyWorkTree :: Key -> FilePath -> Annex Bool
|
||||||
verifyWorkTree key file = do
|
verifyWorkTree key file = do
|
||||||
ifM isDirect ( godirect, goindirect )
|
|
||||||
return True
|
|
||||||
where
|
|
||||||
{- Ensures that files whose content is available are in direct mode. -}
|
|
||||||
godirect = whenM (isJust <$> isAnnexLink file) $ do
|
|
||||||
v <- toDirectGen key file
|
|
||||||
case v of
|
|
||||||
Nothing -> noop
|
|
||||||
Just a -> do
|
|
||||||
showNote "fixing direct mode"
|
|
||||||
a
|
|
||||||
{- Make sure that a pointer file is replaced with its content,
|
{- Make sure that a pointer file is replaced with its content,
|
||||||
- when the content is available. -}
|
- when the content is available. -}
|
||||||
goindirect = do
|
|
||||||
mk <- liftIO $ isPointerFile file
|
mk <- liftIO $ isPointerFile file
|
||||||
case mk of
|
case mk of
|
||||||
Just k | k == key -> whenM (inAnnex key) $ do
|
Just k | k == key -> whenM (inAnnex key) $ do
|
||||||
|
@ -364,11 +339,12 @@ verifyWorkTree key file = do
|
||||||
)
|
)
|
||||||
Database.Keys.storeInodeCaches key [file]
|
Database.Keys.storeInodeCaches key [file]
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
return True
|
||||||
|
|
||||||
{- The size of the data for a key is checked against the size encoded in
|
{- The size of the data for a key is checked against the size encoded in
|
||||||
- the key's metadata, if available.
|
- the key's metadata, if available.
|
||||||
-
|
-
|
||||||
- Not checked when a file is unlocked, or in direct mode.
|
- Not checked when a file is unlocked.
|
||||||
-}
|
-}
|
||||||
checkKeySize :: Key -> KeyStatus -> ActionItem -> Annex Bool
|
checkKeySize :: Key -> KeyStatus -> ActionItem -> Annex Bool
|
||||||
checkKeySize _ KeyUnlockedThin _ = return True
|
checkKeySize _ KeyUnlockedThin _ = return True
|
||||||
|
@ -439,28 +415,15 @@ checkKeyUpgrade _ _ _ (AssociatedFile Nothing) =
|
||||||
- thus when the user modifies the file, the object will be modified and
|
- thus when the user modifies the file, the object will be modified and
|
||||||
- not pass the check, and we don't want to find an error in this case.
|
- not pass the check, and we don't want to find an error in this case.
|
||||||
- So, skip the check if the key is unlocked and modified.
|
- So, skip the check if the key is unlocked and modified.
|
||||||
-
|
|
||||||
- In direct mode this is not done if the file has clearly been modified,
|
|
||||||
- because modification of direct mode files is allowed. It's still done
|
|
||||||
- if the file does not appear modified, to catch disk corruption, etc.
|
|
||||||
-}
|
-}
|
||||||
checkBackend :: Backend -> Key -> KeyStatus -> AssociatedFile -> Annex Bool
|
checkBackend :: Backend -> Key -> KeyStatus -> AssociatedFile -> Annex Bool
|
||||||
checkBackend backend key keystatus afile = go =<< isDirect
|
checkBackend backend key keystatus afile = do
|
||||||
where
|
|
||||||
go False = do
|
|
||||||
content <- calcRepo $ gitAnnexLocation key
|
content <- calcRepo $ gitAnnexLocation key
|
||||||
ifM (pure (isKeyUnlockedThin keystatus) <&&> (not <$> isUnmodified key content))
|
ifM (pure (isKeyUnlockedThin keystatus) <&&> (not <$> isUnmodified key content))
|
||||||
( nocheck
|
( nocheck
|
||||||
, checkBackendOr badContent backend key content ai
|
, checkBackendOr badContent backend key content ai
|
||||||
)
|
)
|
||||||
go True = case afile of
|
where
|
||||||
AssociatedFile Nothing -> nocheck
|
|
||||||
AssociatedFile (Just f) -> checkdirect f
|
|
||||||
checkdirect file = ifM (Direct.goodContent key file)
|
|
||||||
( checkBackendOr' (badContentDirect file) backend key file ai
|
|
||||||
(Direct.goodContent key file)
|
|
||||||
, nocheck
|
|
||||||
)
|
|
||||||
nocheck = return True
|
nocheck = return True
|
||||||
|
|
||||||
ai = mkActionItem (key, afile)
|
ai = mkActionItem (key, afile)
|
||||||
|
@ -475,7 +438,7 @@ checkBackendOr bad backend key file ai =
|
||||||
|
|
||||||
-- The postcheck action is run after the content is verified,
|
-- The postcheck action is run after the content is verified,
|
||||||
-- in order to detect situations where the file is changed while being
|
-- in order to detect situations where the file is changed while being
|
||||||
-- verified (particularly in direct mode).
|
-- verified.
|
||||||
checkBackendOr' :: (Key -> Annex String) -> Backend -> Key -> FilePath -> ActionItem -> Annex Bool -> Annex Bool
|
checkBackendOr' :: (Key -> Annex String) -> Backend -> Key -> FilePath -> ActionItem -> Annex Bool -> Annex Bool
|
||||||
checkBackendOr' bad backend key file ai postcheck =
|
checkBackendOr' bad backend key file ai postcheck =
|
||||||
case Types.Backend.verifyKeyContent backend of
|
case Types.Backend.verifyKeyContent backend of
|
||||||
|
@ -546,14 +509,6 @@ badContent key = do
|
||||||
dest <- moveBad key
|
dest <- moveBad key
|
||||||
return $ "moved to " ++ dest
|
return $ "moved to " ++ dest
|
||||||
|
|
||||||
{- Bad content is left where it is, but we touch the file, so it'll be
|
|
||||||
- committed to a new key. -}
|
|
||||||
badContentDirect :: FilePath -> Key -> Annex String
|
|
||||||
badContentDirect file key = do
|
|
||||||
void $ liftIO $ catchMaybeIO $ touchFile file
|
|
||||||
logStatus key InfoMissing
|
|
||||||
return "left in place for you to examine"
|
|
||||||
|
|
||||||
{- Bad content is dropped from the remote. We have downloaded a copy
|
{- Bad content is dropped from the remote. We have downloaded a copy
|
||||||
- from the remote to a temp file already (in some cases, it's just a
|
- from the remote to a temp file already (in some cases, it's just a
|
||||||
- symlink to a file in the remote). To avoid any further data loss,
|
- symlink to a file in the remote). To avoid any further data loss,
|
||||||
|
@ -714,16 +669,13 @@ isKeyUnlockedThin KeyPresent = False
|
||||||
isKeyUnlockedThin KeyMissing = False
|
isKeyUnlockedThin KeyMissing = False
|
||||||
|
|
||||||
getKeyStatus :: Key -> Annex KeyStatus
|
getKeyStatus :: Key -> Annex KeyStatus
|
||||||
getKeyStatus key = ifM isDirect
|
getKeyStatus key = catchDefaultIO KeyMissing $ do
|
||||||
( return KeyUnlockedThin
|
|
||||||
, catchDefaultIO KeyMissing $ do
|
|
||||||
afs <- not . null <$> Database.Keys.getAssociatedFiles key
|
afs <- not . null <$> Database.Keys.getAssociatedFiles key
|
||||||
obj <- calcRepo $ gitAnnexLocation key
|
obj <- calcRepo $ gitAnnexLocation key
|
||||||
multilink <- ((> 1) . linkCount <$> liftIO (getFileStatus obj))
|
multilink <- ((> 1) . linkCount <$> liftIO (getFileStatus obj))
|
||||||
return $ if multilink && afs
|
return $ if multilink && afs
|
||||||
then KeyUnlockedThin
|
then KeyUnlockedThin
|
||||||
else KeyPresent
|
else KeyPresent
|
||||||
)
|
|
||||||
|
|
||||||
getKeyFileStatus :: Key -> FilePath -> Annex KeyStatus
|
getKeyFileStatus :: Key -> FilePath -> Annex KeyStatus
|
||||||
getKeyFileStatus key file = do
|
getKeyFileStatus key file = do
|
||||||
|
|
|
@ -155,7 +155,6 @@ data FuzzAction
|
||||||
= FuzzAdd FuzzFile
|
= FuzzAdd FuzzFile
|
||||||
| FuzzDelete FuzzFile
|
| FuzzDelete FuzzFile
|
||||||
| FuzzMove FuzzFile FuzzFile
|
| FuzzMove FuzzFile FuzzFile
|
||||||
| FuzzModify FuzzFile
|
|
||||||
| FuzzDeleteDir FuzzDir
|
| FuzzDeleteDir FuzzDir
|
||||||
| FuzzMoveDir FuzzDir FuzzDir
|
| FuzzMoveDir FuzzDir FuzzDir
|
||||||
| FuzzPause Delay
|
| FuzzPause Delay
|
||||||
|
@ -166,7 +165,6 @@ instance Arbitrary FuzzAction where
|
||||||
[ (50, FuzzAdd <$> arbitrary)
|
[ (50, FuzzAdd <$> arbitrary)
|
||||||
, (50, FuzzDelete <$> arbitrary)
|
, (50, FuzzDelete <$> arbitrary)
|
||||||
, (10, FuzzMove <$> arbitrary <*> arbitrary)
|
, (10, FuzzMove <$> arbitrary <*> arbitrary)
|
||||||
, (10, FuzzModify <$> arbitrary)
|
|
||||||
, (10, FuzzDeleteDir <$> arbitrary)
|
, (10, FuzzDeleteDir <$> arbitrary)
|
||||||
, (10, FuzzMoveDir <$> arbitrary <*> arbitrary)
|
, (10, FuzzMoveDir <$> arbitrary <*> arbitrary)
|
||||||
, (10, FuzzPause <$> arbitrary)
|
, (10, FuzzPause <$> arbitrary)
|
||||||
|
@ -180,9 +178,6 @@ runFuzzAction (FuzzAdd (FuzzFile f)) = liftIO $ do
|
||||||
runFuzzAction (FuzzDelete (FuzzFile f)) = liftIO $ nukeFile f
|
runFuzzAction (FuzzDelete (FuzzFile f)) = liftIO $ nukeFile f
|
||||||
runFuzzAction (FuzzMove (FuzzFile src) (FuzzFile dest)) = liftIO $
|
runFuzzAction (FuzzMove (FuzzFile src) (FuzzFile dest)) = liftIO $
|
||||||
rename src dest
|
rename src dest
|
||||||
runFuzzAction (FuzzModify (FuzzFile f)) = whenM isDirect $ liftIO $ do
|
|
||||||
n <- getStdRandom random :: IO Int
|
|
||||||
appendFile f $ show n ++ "\n"
|
|
||||||
runFuzzAction (FuzzDeleteDir (FuzzDir d)) = liftIO $
|
runFuzzAction (FuzzDeleteDir (FuzzDir d)) = liftIO $
|
||||||
removeDirectoryRecursive d
|
removeDirectoryRecursive d
|
||||||
runFuzzAction (FuzzMoveDir (FuzzDir src) (FuzzDir dest)) = liftIO $
|
runFuzzAction (FuzzMoveDir (FuzzDir src) (FuzzDir dest)) = liftIO $
|
||||||
|
@ -216,9 +211,6 @@ genFuzzAction = do
|
||||||
FuzzDeleteDir _ -> do
|
FuzzDeleteDir _ -> do
|
||||||
d <- liftIO existingDir
|
d <- liftIO existingDir
|
||||||
maybe genFuzzAction (return . FuzzDeleteDir) d
|
maybe genFuzzAction (return . FuzzDeleteDir) d
|
||||||
FuzzModify _ -> do
|
|
||||||
f <- liftIO $ existingFile 0 ""
|
|
||||||
maybe genFuzzAction (return . FuzzModify) f
|
|
||||||
FuzzPause _ -> return tmpl
|
FuzzPause _ -> return tmpl
|
||||||
|
|
||||||
existingFile :: Int -> FilePath -> IO (Maybe FuzzFile)
|
existingFile :: Int -> FilePath -> IO (Maybe FuzzFile)
|
||||||
|
|
|
@ -31,7 +31,6 @@ import Logs.Trust
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
import Annex.NumCopies
|
import Annex.NumCopies
|
||||||
import Remote
|
import Remote
|
||||||
import Config
|
|
||||||
import Git.Config (boolConfig)
|
import Git.Config (boolConfig)
|
||||||
import qualified Git.LsTree as LsTree
|
import qualified Git.LsTree as LsTree
|
||||||
import Utility.Percentage
|
import Utility.Percentage
|
||||||
|
@ -318,13 +317,10 @@ showStat s = maybe noop calc =<< s
|
||||||
|
|
||||||
repository_mode :: Stat
|
repository_mode :: Stat
|
||||||
repository_mode = simpleStat "repository mode" $ lift $
|
repository_mode = simpleStat "repository mode" $ lift $
|
||||||
ifM isDirect
|
ifM (fromRepo Git.repoIsLocalBare)
|
||||||
( return "direct"
|
|
||||||
, ifM (fromRepo Git.repoIsLocalBare)
|
|
||||||
( return "bare"
|
( return "bare"
|
||||||
, return "indirect"
|
, return "indirect"
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
repo_list :: TrustLevel -> Stat
|
repo_list :: TrustLevel -> Stat
|
||||||
repo_list level = stat n $ nojson $ lift $ do
|
repo_list level = stat n $ nojson $ lift $ do
|
||||||
|
|
|
@ -23,7 +23,7 @@ import Logs.Location
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $ withGlobalOptions [jsonOptions, annexedMatchingOptions] $
|
cmd = withGlobalOptions [jsonOptions, annexedMatchingOptions] $
|
||||||
command "lock" SectionCommon
|
command "lock" SectionCommon
|
||||||
"undo unlock command"
|
"undo unlock command"
|
||||||
paramPaths (withParams seek)
|
paramPaths (withParams seek)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import Logs.Web
|
||||||
import Utility.Metered
|
import Utility.Metered
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $ withGlobalOptions [annexedMatchingOptions] $
|
cmd = withGlobalOptions [annexedMatchingOptions] $
|
||||||
command "migrate" SectionUtility
|
command "migrate" SectionUtility
|
||||||
"switch data to different backend"
|
"switch data to different backend"
|
||||||
paramPaths (withParams seek)
|
paramPaths (withParams seek)
|
||||||
|
|
|
@ -27,7 +27,6 @@ import Utility.Hash
|
||||||
import Utility.Tmp
|
import Utility.Tmp
|
||||||
import Utility.Tmp.Dir
|
import Utility.Tmp.Dir
|
||||||
import Utility.Process.Transcript
|
import Utility.Process.Transcript
|
||||||
import Config
|
|
||||||
|
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import qualified Data.ByteString.Lazy.UTF8 as B8
|
import qualified Data.ByteString.Lazy.UTF8 as B8
|
||||||
|
@ -128,8 +127,6 @@ send ups fs = do
|
||||||
-- In a direct mode repository, the annex objects do not have
|
-- In a direct mode repository, the annex objects do not have
|
||||||
-- the names of keys, and would have to be copied, which is too
|
-- the names of keys, and would have to be copied, which is too
|
||||||
-- expensive.
|
-- expensive.
|
||||||
whenM isDirect $
|
|
||||||
giveup "Sorry, multicast send cannot be done from a direct mode repository."
|
|
||||||
starting "sending files" (ActionItemOther Nothing) $
|
starting "sending files" (ActionItemOther Nothing) $
|
||||||
withTmpFile "send" $ \t h -> do
|
withTmpFile "send" $ \t h -> do
|
||||||
fs' <- seekHelper LsFiles.inRepo =<< workTreeItems fs
|
fs' <- seekHelper LsFiles.inRepo =<< workTreeItems fs
|
||||||
|
|
|
@ -10,11 +10,9 @@
|
||||||
module Command.PreCommit where
|
module Command.PreCommit where
|
||||||
|
|
||||||
import Command
|
import Command
|
||||||
import Config
|
|
||||||
import qualified Command.Add
|
import qualified Command.Add
|
||||||
import qualified Command.Fix
|
import qualified Command.Fix
|
||||||
import qualified Command.Smudge
|
import qualified Command.Smudge
|
||||||
import Annex.Direct
|
|
||||||
import Annex.Hook
|
import Annex.Hook
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Annex.View
|
import Annex.View
|
||||||
|
@ -38,12 +36,7 @@ cmd = command "pre-commit" SectionPlumbing
|
||||||
(withParams seek)
|
(withParams seek)
|
||||||
|
|
||||||
seek :: CmdParams -> CommandSeek
|
seek :: CmdParams -> CommandSeek
|
||||||
seek ps = lockPreCommitHook $ ifM isDirect
|
seek ps = lockPreCommitHook $ do
|
||||||
( do
|
|
||||||
-- update direct mode mappings for committed files
|
|
||||||
withWords (commandAction . startDirect) ps
|
|
||||||
runAnnexHook preCommitAnnexHook
|
|
||||||
, do
|
|
||||||
ifM (not <$> versionSupportsUnlockedPointers <&&> liftIO Git.haveFalseIndex)
|
ifM (not <$> versionSupportsUnlockedPointers <&&> liftIO Git.haveFalseIndex)
|
||||||
( do
|
( do
|
||||||
(fs, cleanup) <- inRepo $ Git.typeChangedStaged ps
|
(fs, cleanup) <- inRepo $ Git.typeChangedStaged ps
|
||||||
|
@ -72,7 +65,9 @@ seek ps = lockPreCommitHook $ ifM isDirect
|
||||||
, withFilesOldUnlockedToBeCommitted (commandAction . startInjectUnlocked) l
|
, withFilesOldUnlockedToBeCommitted (commandAction . startInjectUnlocked) l
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
runAnnexHook preCommitAnnexHook
|
runAnnexHook preCommitAnnexHook
|
||||||
|
|
||||||
-- committing changes to a view updates metadata
|
-- committing changes to a view updates metadata
|
||||||
mv <- currentView
|
mv <- currentView
|
||||||
case mv of
|
case mv of
|
||||||
|
@ -80,8 +75,6 @@ seek ps = lockPreCommitHook $ ifM isDirect
|
||||||
Just v -> withViewChanges
|
Just v -> withViewChanges
|
||||||
(addViewMetaData v)
|
(addViewMetaData v)
|
||||||
(removeViewMetaData v)
|
(removeViewMetaData v)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
startInjectUnlocked :: FilePath -> CommandStart
|
startInjectUnlocked :: FilePath -> CommandStart
|
||||||
startInjectUnlocked f = startingCustomOutput (ActionItemOther Nothing) $ do
|
startInjectUnlocked f = startingCustomOutput (ActionItemOther Nothing) $ do
|
||||||
|
@ -89,10 +82,6 @@ startInjectUnlocked f = startingCustomOutput (ActionItemOther Nothing) $ do
|
||||||
error $ "failed to add " ++ f ++ "; canceling commit"
|
error $ "failed to add " ++ f ++ "; canceling commit"
|
||||||
next $ return True
|
next $ return True
|
||||||
|
|
||||||
startDirect :: [String] -> CommandStart
|
|
||||||
startDirect _ = startingCustomOutput (ActionItemOther Nothing) $
|
|
||||||
next preCommitDirect
|
|
||||||
|
|
||||||
addViewMetaData :: View -> ViewedFile -> Key -> CommandStart
|
addViewMetaData :: View -> ViewedFile -> Key -> CommandStart
|
||||||
addViewMetaData v f k = starting "metadata" (mkActionItem (k, f)) $
|
addViewMetaData v f k = starting "metadata" (mkActionItem (k, f)) $
|
||||||
next $ changeMetaData k $ fromView v f
|
next $ changeMetaData k $ fromView v f
|
||||||
|
|
|
@ -21,8 +21,7 @@ import Annex.InodeSentinal
|
||||||
import Utility.InodeCache
|
import Utility.InodeCache
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $
|
cmd = command "rekey" SectionPlumbing
|
||||||
command "rekey" SectionPlumbing
|
|
||||||
"change keys used for files"
|
"change keys used for files"
|
||||||
(paramRepeating $ paramPair paramPath paramKey)
|
(paramRepeating $ paramPair paramPath paramKey)
|
||||||
(seek <$$> optParser)
|
(seek <$$> optParser)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import Command.FromKey (mkKey)
|
||||||
import qualified Remote
|
import qualified Remote
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notDirect $ notBareRepo $
|
cmd = notBareRepo $
|
||||||
command "registerurl"
|
command "registerurl"
|
||||||
SectionPlumbing "registers an url for a key"
|
SectionPlumbing "registers an url for a key"
|
||||||
(paramPair paramKey paramUrl)
|
(paramPair paramKey paramUrl)
|
||||||
|
|
|
@ -8,11 +8,7 @@
|
||||||
module Command.Status where
|
module Command.Status where
|
||||||
|
|
||||||
import Command
|
import Command
|
||||||
import Annex.CatFile
|
|
||||||
import Annex.Content.Direct
|
|
||||||
import Config
|
|
||||||
import Git.Status
|
import Git.Status
|
||||||
import qualified Git.Ref
|
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
|
@ -42,10 +38,7 @@ seek o = withWords (commandAction . start o) (statusFiles o)
|
||||||
start :: StatusOptions -> [FilePath] -> CommandStart
|
start :: StatusOptions -> [FilePath] -> CommandStart
|
||||||
start o locs = do
|
start o locs = do
|
||||||
(l, cleanup) <- inRepo $ getStatus ps locs
|
(l, cleanup) <- inRepo $ getStatus ps locs
|
||||||
getstatus <- ifM isDirect
|
let getstatus = pure . simplifiedStatus
|
||||||
( return (maybe (pure Nothing) statusDirect . simplifiedStatus)
|
|
||||||
, return (pure . simplifiedStatus)
|
|
||||||
)
|
|
||||||
forM_ l $ \s -> maybe noop displayStatus =<< getstatus s
|
forM_ l $ \s -> maybe noop displayStatus =<< getstatus s
|
||||||
ifM (liftIO cleanup)
|
ifM (liftIO cleanup)
|
||||||
( stop
|
( stop
|
||||||
|
@ -71,38 +64,3 @@ displayStatus s = do
|
||||||
f <- liftIO $ relPathCwdToFile absf
|
f <- liftIO $ relPathCwdToFile absf
|
||||||
unlessM (showFullJSON $ JSONChunk [("status", [c]), ("file", f)]) $
|
unlessM (showFullJSON $ JSONChunk [("status", [c]), ("file", f)]) $
|
||||||
liftIO $ putStrLn $ [c] ++ " " ++ f
|
liftIO $ putStrLn $ [c] ++ " " ++ f
|
||||||
|
|
||||||
-- Git thinks that present direct mode files are typechanged.
|
|
||||||
-- (On crippled filesystems, git instead thinks they're modified.)
|
|
||||||
-- Check their content to see if they are modified or not.
|
|
||||||
statusDirect :: Status -> Annex (Maybe Status)
|
|
||||||
statusDirect (TypeChanged t) = statusDirect' t
|
|
||||||
statusDirect s@(Modified t) = ifM crippledFileSystem
|
|
||||||
( statusDirect' t
|
|
||||||
, pure (Just s)
|
|
||||||
)
|
|
||||||
statusDirect s = pure (Just s)
|
|
||||||
|
|
||||||
statusDirect' :: TopFilePath -> Annex (Maybe Status)
|
|
||||||
statusDirect' t = do
|
|
||||||
absf <- fromRepo $ fromTopFilePath t
|
|
||||||
f <- liftIO $ relPathCwdToFile absf
|
|
||||||
v <- liftIO (catchMaybeIO $ getFileStatus f)
|
|
||||||
case v of
|
|
||||||
Nothing -> return $ Just $ Deleted t
|
|
||||||
Just s
|
|
||||||
| not (isSymbolicLink s) ->
|
|
||||||
checkkey f s =<< catKeyFile f
|
|
||||||
| otherwise -> Just <$> checkNew f t
|
|
||||||
where
|
|
||||||
checkkey f s (Just k) = ifM (sameFileStatus k f s)
|
|
||||||
( return Nothing
|
|
||||||
, return $ Just $ Modified t
|
|
||||||
)
|
|
||||||
checkkey f _ Nothing = Just <$> checkNew f t
|
|
||||||
|
|
||||||
checkNew :: FilePath -> TopFilePath -> Annex Status
|
|
||||||
checkNew f t = ifM (isJust <$> catObjectDetails (Git.Ref.fileRef f))
|
|
||||||
( return (Modified t)
|
|
||||||
, return (Untracked t)
|
|
||||||
)
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ import qualified Annex
|
||||||
import qualified Annex.Branch
|
import qualified Annex.Branch
|
||||||
import qualified Remote
|
import qualified Remote
|
||||||
import qualified Types.Remote as Remote
|
import qualified Types.Remote as Remote
|
||||||
import Annex.Direct
|
|
||||||
import Annex.Hook
|
import Annex.Hook
|
||||||
import qualified Git.Command
|
import qualified Git.Command
|
||||||
import qualified Git.LsFiles as LsFiles
|
import qualified Git.LsFiles as LsFiles
|
||||||
|
@ -250,7 +249,7 @@ merge currbranch mergeconfig resolvemergeoverride commitmode tomerge = case curr
|
||||||
ResolveMergeOverride False -> return False
|
ResolveMergeOverride False -> return False
|
||||||
|
|
||||||
syncBranch :: Git.Branch -> Git.Branch
|
syncBranch :: Git.Branch -> Git.Branch
|
||||||
syncBranch = Git.Ref.underBase "refs/heads/synced" . fromDirectBranch . fromAdjustedBranch
|
syncBranch = Git.Ref.underBase "refs/heads/synced" . fromAdjustedBranch
|
||||||
|
|
||||||
remoteBranch :: Remote -> Git.Ref -> Git.Ref
|
remoteBranch :: Remote -> Git.Ref -> Git.Ref
|
||||||
remoteBranch remote = Git.Ref.underBase $ "refs/remotes/" ++ Remote.name remote
|
remoteBranch remote = Git.Ref.underBase $ "refs/remotes/" ++ Remote.name remote
|
||||||
|
@ -286,12 +285,7 @@ commit :: SyncOptions -> CommandStart
|
||||||
commit o = stopUnless shouldcommit $ starting "commit" (ActionItemOther Nothing) $ do
|
commit o = stopUnless shouldcommit $ starting "commit" (ActionItemOther Nothing) $ do
|
||||||
commitmessage <- maybe commitMsg return (messageOption o)
|
commitmessage <- maybe commitMsg return (messageOption o)
|
||||||
Annex.Branch.commit =<< Annex.Branch.commitMessage
|
Annex.Branch.commit =<< Annex.Branch.commitMessage
|
||||||
next $ ifM isDirect
|
next $ do
|
||||||
( do
|
|
||||||
void stageDirect
|
|
||||||
void preCommitDirect
|
|
||||||
commitStaged Git.Branch.ManualCommit commitmessage
|
|
||||||
, do
|
|
||||||
showOutput
|
showOutput
|
||||||
void $ inRepo $ Git.Branch.commitCommand Git.Branch.ManualCommit
|
void $ inRepo $ Git.Branch.commitCommand Git.Branch.ManualCommit
|
||||||
[ Param "-a"
|
[ Param "-a"
|
||||||
|
@ -299,7 +293,6 @@ commit o = stopUnless shouldcommit $ starting "commit" (ActionItemOther Nothing)
|
||||||
, Param commitmessage
|
, Param commitmessage
|
||||||
]
|
]
|
||||||
return True
|
return True
|
||||||
)
|
|
||||||
where
|
where
|
||||||
shouldcommit = pure (commitOption o)
|
shouldcommit = pure (commitOption o)
|
||||||
<||> (pure (not (noCommitOption o)) <&&> getGitConfigVal annexAutoCommit)
|
<||> (pure (not (noCommitOption o)) <&&> getGitConfigVal annexAutoCommit)
|
||||||
|
@ -378,12 +371,6 @@ updateBranches (Just branch, madj) = do
|
||||||
-- Update the sync branch to match the new state of the branch
|
-- Update the sync branch to match the new state of the branch
|
||||||
inRepo $ updateBranch (syncBranch branch) branch
|
inRepo $ updateBranch (syncBranch branch) branch
|
||||||
|
|
||||||
-- In direct mode, we're operating on some special direct mode
|
|
||||||
-- branch, rather than the intended branch, so update the intended
|
|
||||||
-- branch.
|
|
||||||
whenM isDirect $
|
|
||||||
inRepo $ updateBranch (fromDirectBranch branch) branch
|
|
||||||
|
|
||||||
updateBranch :: Git.Branch -> Git.Branch -> Git.Repo -> IO ()
|
updateBranch :: Git.Branch -> Git.Branch -> Git.Repo -> IO ()
|
||||||
updateBranch syncbranch updateto g =
|
updateBranch syncbranch updateto g =
|
||||||
unlessM go $ giveup $ "failed to update " ++ Git.fromRef syncbranch
|
unlessM go $ giveup $ "failed to update " ++ Git.fromRef syncbranch
|
||||||
|
@ -449,7 +436,7 @@ mergeRemote remote currbranch mergeconfig resolvemergeoverride = ifM isBareRepo
|
||||||
(mapM (merge currbranch mergeconfig resolvemergeoverride Git.Branch.ManualCommit . remoteBranch remote) =<< getlist)
|
(mapM (merge currbranch mergeconfig resolvemergeoverride Git.Branch.ManualCommit . remoteBranch remote) =<< getlist)
|
||||||
tomerge = filterM (changed remote)
|
tomerge = filterM (changed remote)
|
||||||
branchlist Nothing = []
|
branchlist Nothing = []
|
||||||
branchlist (Just branch) = [fromDirectBranch (fromAdjustedBranch branch), syncBranch branch]
|
branchlist (Just branch) = [fromAdjustedBranch branch, syncBranch branch]
|
||||||
|
|
||||||
pushRemote :: SyncOptions -> Remote -> CurrBranch -> CommandStart
|
pushRemote :: SyncOptions -> Remote -> CurrBranch -> CommandStart
|
||||||
pushRemote _o _remote (Nothing, _) = stop
|
pushRemote _o _remote (Nothing, _) = stop
|
||||||
|
@ -540,7 +527,7 @@ pushBranch remote branch g = directpush `after` annexpush `after` syncpush
|
||||||
-- receive.denyCurrentBranch=updateInstead -- the user
|
-- receive.denyCurrentBranch=updateInstead -- the user
|
||||||
-- will want to see that one.
|
-- will want to see that one.
|
||||||
let p = flip Git.Command.gitCreateProcess g $ pushparams
|
let p = flip Git.Command.gitCreateProcess g $ pushparams
|
||||||
[ Git.fromRef $ Git.Ref.base $ fromDirectBranch $ fromAdjustedBranch branch ]
|
[ Git.fromRef $ Git.Ref.base $ fromAdjustedBranch branch ]
|
||||||
(transcript, ok) <- processTranscript' p Nothing
|
(transcript, ok) <- processTranscript' p Nothing
|
||||||
when (not ok && not ("denyCurrentBranch" `isInfixOf` transcript)) $
|
when (not ok && not ("denyCurrentBranch" `isInfixOf` transcript)) $
|
||||||
hPutStr stderr transcript
|
hPutStr stderr transcript
|
||||||
|
|
|
@ -8,11 +8,9 @@
|
||||||
module Command.Unannex where
|
module Command.Unannex where
|
||||||
|
|
||||||
import Command
|
import Command
|
||||||
import Config
|
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import Annex.Perms
|
import Annex.Perms
|
||||||
import Annex.Content.Direct
|
|
||||||
import Annex.Version
|
import Annex.Version
|
||||||
import qualified Git.Command
|
import qualified Git.Command
|
||||||
import qualified Git.Branch
|
import qualified Git.Branch
|
||||||
|
@ -34,7 +32,7 @@ seek ps = wrapUnannex $
|
||||||
(withFilesInGit $ commandAction . whenAnnexed start) =<< workTreeItems ps
|
(withFilesInGit $ commandAction . whenAnnexed start) =<< workTreeItems ps
|
||||||
|
|
||||||
wrapUnannex :: Annex a -> Annex a
|
wrapUnannex :: Annex a -> Annex a
|
||||||
wrapUnannex a = ifM (versionSupportsUnlockedPointers <||> isDirect)
|
wrapUnannex a = ifM versionSupportsUnlockedPointers
|
||||||
( a
|
( a
|
||||||
{- Run with the pre-commit hook disabled, to avoid confusing
|
{- Run with the pre-commit hook disabled, to avoid confusing
|
||||||
- behavior if an unannexed file is added back to git as
|
- behavior if an unannexed file is added back to git as
|
||||||
|
@ -68,13 +66,10 @@ wrapUnannex a = ifM (versionSupportsUnlockedPointers <||> isDirect)
|
||||||
start :: FilePath -> Key -> CommandStart
|
start :: FilePath -> Key -> CommandStart
|
||||||
start file key = stopUnless (inAnnex key) $
|
start file key = stopUnless (inAnnex key) $
|
||||||
starting "unannex" (mkActionItem (key, file)) $
|
starting "unannex" (mkActionItem (key, file)) $
|
||||||
ifM isDirect
|
perform file key
|
||||||
( performDirect file key
|
|
||||||
, performIndirect file key
|
|
||||||
)
|
|
||||||
|
|
||||||
performIndirect :: FilePath -> Key -> CommandPerform
|
perform :: FilePath -> Key -> CommandPerform
|
||||||
performIndirect file key = do
|
perform file key = do
|
||||||
liftIO $ removeFile file
|
liftIO $ removeFile file
|
||||||
inRepo $ Git.Command.run
|
inRepo $ Git.Command.run
|
||||||
[ Param "rm"
|
[ Param "rm"
|
||||||
|
@ -84,10 +79,10 @@ performIndirect file key = do
|
||||||
, Param "--"
|
, Param "--"
|
||||||
, File file
|
, File file
|
||||||
]
|
]
|
||||||
next $ cleanupIndirect file key
|
next $ cleanup file key
|
||||||
|
|
||||||
cleanupIndirect :: FilePath -> Key -> CommandCleanup
|
cleanup :: FilePath -> Key -> CommandCleanup
|
||||||
cleanupIndirect file key = do
|
cleanup file key = do
|
||||||
Database.Keys.removeAssociatedFile key =<< inRepo (toTopFilePath file)
|
Database.Keys.removeAssociatedFile key =<< inRepo (toTopFilePath file)
|
||||||
src <- calcRepo $ gitAnnexLocation key
|
src <- calcRepo $ gitAnnexLocation key
|
||||||
ifM (Annex.getState Annex.fast)
|
ifM (Annex.getState Annex.fast)
|
||||||
|
@ -112,28 +107,3 @@ cleanupIndirect file key = do
|
||||||
( return True
|
( return True
|
||||||
, copyfrom src
|
, copyfrom src
|
||||||
)
|
)
|
||||||
|
|
||||||
performDirect :: FilePath -> Key -> CommandPerform
|
|
||||||
performDirect file key = do
|
|
||||||
-- --force is needed when the file is not committed
|
|
||||||
inRepo $ Git.Command.run
|
|
||||||
[ Param "rm"
|
|
||||||
, Param "--cached"
|
|
||||||
, Param "--force"
|
|
||||||
, Param "--quiet"
|
|
||||||
, Param "--"
|
|
||||||
, File file
|
|
||||||
]
|
|
||||||
next $ cleanupDirect file key
|
|
||||||
|
|
||||||
{- The direct mode file is not touched during unannex, so the content
|
|
||||||
- is already where it needs to be, so this does not need to do anything
|
|
||||||
- except remove it from the associated file map (which also updates
|
|
||||||
- the location log if this was the last copy), and, if this was the last
|
|
||||||
- associated file, remove the inode cache. -}
|
|
||||||
cleanupDirect :: FilePath -> Key -> CommandCleanup
|
|
||||||
cleanupDirect file key = do
|
|
||||||
fs <- removeAssociatedFile key file
|
|
||||||
when (null fs) $
|
|
||||||
removeInodeCache key
|
|
||||||
return True
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ module Command.Undo where
|
||||||
|
|
||||||
import Command
|
import Command
|
||||||
import Config
|
import Config
|
||||||
import Annex.Direct
|
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
import Git.DiffTree
|
import Git.DiffTree
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
|
@ -37,9 +36,7 @@ seek ps = do
|
||||||
|
|
||||||
-- Committing staged changes before undo allows later
|
-- Committing staged changes before undo allows later
|
||||||
-- undoing the undo. It would be nicer to only commit staged
|
-- undoing the undo. It would be nicer to only commit staged
|
||||||
-- changes to the specified files, rather than all staged changes,
|
-- changes to the specified files, rather than all staged changes.
|
||||||
-- but that is difficult to do; a partial git-commit can't be done
|
|
||||||
-- in direct mode.
|
|
||||||
void $ Command.Sync.commitStaged Git.Branch.ManualCommit
|
void $ Command.Sync.commitStaged Git.Branch.ManualCommit
|
||||||
"commit before undo"
|
"commit before undo"
|
||||||
|
|
||||||
|
@ -68,16 +65,10 @@ perform p = do
|
||||||
|
|
||||||
forM_ removals $ \di -> do
|
forM_ removals $ \di -> do
|
||||||
f <- mkrel di
|
f <- mkrel di
|
||||||
whenM isDirect $
|
|
||||||
maybe noop (`removeDirect` f)
|
|
||||||
=<< catKey (srcsha di)
|
|
||||||
liftIO $ nukeFile f
|
liftIO $ nukeFile f
|
||||||
|
|
||||||
forM_ adds $ \di -> do
|
forM_ adds $ \di -> do
|
||||||
f <- mkrel di
|
f <- mkrel di
|
||||||
inRepo $ Git.run [Param "checkout", Param "--", File f]
|
inRepo $ Git.run [Param "checkout", Param "--", File f]
|
||||||
whenM isDirect $
|
|
||||||
maybe noop (`toDirect` f)
|
|
||||||
=<< catKey (dstsha di)
|
|
||||||
|
|
||||||
next $ liftIO cleanup
|
next $ liftIO cleanup
|
||||||
|
|
|
@ -25,8 +25,7 @@ editcmd :: Command
|
||||||
editcmd = mkcmd "edit" "same as unlock"
|
editcmd = mkcmd "edit" "same as unlock"
|
||||||
|
|
||||||
mkcmd :: String -> String -> Command
|
mkcmd :: String -> String -> Command
|
||||||
mkcmd n d = notDirect $
|
mkcmd n d = withGlobalOptions [jsonOptions, annexedMatchingOptions] $
|
||||||
withGlobalOptions [jsonOptions, annexedMatchingOptions] $
|
|
||||||
command n SectionCommon d paramPaths (withParams seek)
|
command n SectionCommon d paramPaths (withParams seek)
|
||||||
|
|
||||||
seek :: CmdParams -> CommandSeek
|
seek :: CmdParams -> CommandSeek
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Annex.View
|
||||||
import Command.View (checkoutViewBranch)
|
import Command.View (checkoutViewBranch)
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notBareRepo $ notDirect $
|
cmd = notBareRepo $
|
||||||
command "vadd" SectionMetaData
|
command "vadd" SectionMetaData
|
||||||
"add subdirs to current view"
|
"add subdirs to current view"
|
||||||
(paramRepeating "FIELD=GLOB")
|
(paramRepeating "FIELD=GLOB")
|
||||||
|
|
|
@ -14,7 +14,7 @@ import Logs.View
|
||||||
import Command.View (checkoutViewBranch)
|
import Command.View (checkoutViewBranch)
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notBareRepo $ notDirect $
|
cmd = notBareRepo $
|
||||||
command "vcycle" SectionMetaData
|
command "vcycle" SectionMetaData
|
||||||
"switch view to next layout"
|
"switch view to next layout"
|
||||||
paramNothing (withParams seek)
|
paramNothing (withParams seek)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Annex.View
|
||||||
import Command.View (paramView, checkoutViewBranch)
|
import Command.View (paramView, checkoutViewBranch)
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notBareRepo $ notDirect $
|
cmd = notBareRepo $
|
||||||
command "vfilter" SectionMetaData "filter current view"
|
command "vfilter" SectionMetaData "filter current view"
|
||||||
paramView (withParams seek)
|
paramView (withParams seek)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import Logs.View
|
||||||
import Command.View (checkoutViewBranch)
|
import Command.View (checkoutViewBranch)
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notBareRepo $ notDirect $
|
cmd = notBareRepo $
|
||||||
command "vpop" SectionMetaData "switch back to previous view"
|
command "vpop" SectionMetaData "switch back to previous view"
|
||||||
paramNumber (withParams seek)
|
paramNumber (withParams seek)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import Annex.View
|
||||||
import Logs.View
|
import Logs.View
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
cmd = notBareRepo $ notDirect $
|
cmd = notBareRepo $
|
||||||
command "view" SectionMetaData "enter a view branch"
|
command "view" SectionMetaData "enter a view branch"
|
||||||
paramView (withParams seek)
|
paramView (withParams seek)
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,8 @@
|
||||||
module Upgrade.V4 where
|
module Upgrade.V4 where
|
||||||
|
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
import Config
|
|
||||||
import Annex.Direct
|
|
||||||
|
|
||||||
{- Direct mode only upgrade. v4 to v5 indirect update is a no-op -}
|
{- Was only used for direct mode upgrade. v4 to v5 indirect update is a no-op,
|
||||||
|
- and direct mode is no longer supported, so nothing needs to be done. -}
|
||||||
upgrade :: Bool -> Annex Bool
|
upgrade :: Bool -> Annex Bool
|
||||||
upgrade automatic = ifM isDirect
|
upgrade _automatic = return True
|
||||||
( do
|
|
||||||
unless automatic $
|
|
||||||
showAction "v4 to v5"
|
|
||||||
setDirect True
|
|
||||||
return True
|
|
||||||
, return True
|
|
||||||
)
|
|
||||||
|
|
|
@ -13,11 +13,11 @@ import Config
|
||||||
import Config.Smudge
|
import Config.Smudge
|
||||||
import Annex.InodeSentinal
|
import Annex.InodeSentinal
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Annex.Direct
|
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
import Annex.WorkTree
|
import Annex.WorkTree
|
||||||
import qualified Database.Keys
|
import qualified Database.Keys
|
||||||
|
import qualified Annex.Direct as Direct
|
||||||
import qualified Annex.Content.Direct as Direct
|
import qualified Annex.Content.Direct as Direct
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import qualified Git.LsFiles
|
import qualified Git.LsFiles
|
||||||
|
@ -48,7 +48,7 @@ upgrade automatic = do
|
||||||
-- locking down files as they were added. In v6, it's used more
|
-- locking down files as they were added. In v6, it's used more
|
||||||
-- extensively, so make sure it exists, since old repos that didn't
|
-- extensively, so make sure it exists, since old repos that didn't
|
||||||
-- use direct mode may not have created it.
|
-- use direct mode may not have created it.
|
||||||
unlessM (isDirect) $
|
unlessM isDirect $
|
||||||
createInodeSentinalFile True
|
createInodeSentinalFile True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -62,12 +62,12 @@ convertDirect automatic = do
|
||||||
{- Since upgrade from direct mode changes how files
|
{- Since upgrade from direct mode changes how files
|
||||||
- are represented in git, by checking out an adjusted
|
- are represented in git, by checking out an adjusted
|
||||||
- branch, commit any changes in the work tree first. -}
|
- branch, commit any changes in the work tree first. -}
|
||||||
whenM stageDirect $ do
|
whenM Direct.stageDirect $ do
|
||||||
unless automatic $
|
unless automatic $
|
||||||
showAction "committing first"
|
showAction "committing first"
|
||||||
upgradeDirectCommit automatic
|
upgradeDirectCommit automatic
|
||||||
"commit before upgrade to annex.version 6"
|
"commit before upgrade to annex.version 6"
|
||||||
setDirect False
|
Direct.setIndirect
|
||||||
cur <- fromMaybe (error "Somehow no branch is checked out")
|
cur <- fromMaybe (error "Somehow no branch is checked out")
|
||||||
<$> inRepo Git.Branch.current
|
<$> inRepo Git.Branch.current
|
||||||
upgradeDirectWorkTree
|
upgradeDirectWorkTree
|
||||||
|
|
Loading…
Add table
Reference in a new issue