annex.skipunknown with transition plan

Added annex.skipunknown git config, that can be set to false to change the
behavior of commands like `git annex get foo*`, to not skip over files/dirs
that are not checked into git and are explicitly listed in the command
line.

Significant complexity was needed to handle git-annex add, which uses some
git ls-files calls, but needs to not use --error-unmatch because of course
the files are not known to git.

annex.skipunknown is planned to change to default to false in a
git-annex release in early 2022. There's a todo for that.
This commit is contained in:
Joey Hess 2020-05-28 15:55:17 -04:00
parent 5b28a37ea1
commit 89b2542d3c
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
42 changed files with 271 additions and 169 deletions

View file

@ -120,7 +120,7 @@ resolveMerge us them inoverlay = do
void $ liftIO cleanup
unless inoverlay $ do
(deleted, cleanup2) <- inRepo (LsFiles.deleted [top])
(deleted, cleanup2) <- inRepo (LsFiles.deleted [] [top])
unless (null deleted) $
Annex.Queue.addCommand "rm"
[Param "--quiet", Param "-f", Param "--"]
@ -130,7 +130,8 @@ resolveMerge us them inoverlay = do
when merged $ do
Annex.Queue.flush
unless inoverlay $ do
unstagedmap <- inodeMap $ inRepo $ LsFiles.notInRepo False [top]
unstagedmap <- inodeMap $ inRepo $
LsFiles.notInRepo [] False [top]
cleanConflictCruft mergedks' mergedfs' unstagedmap
showLongNote "Merge conflict was automatically resolved; you may want to examine the result."
return merged

View file

@ -152,7 +152,7 @@ dailyCheck urlrenderer = do
batchmaker <- liftIO getBatchCommandMaker
-- Find old unstaged symlinks, and add them to git.
(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo False ["."] g
(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo [] False ["."] g
now <- liftIO getPOSIXTime
forM_ unstaged $ \file -> do
let file' = fromRawFilePath file

View file

@ -128,7 +128,7 @@ expensiveScan urlrenderer rs = batch <~> do
<$> filterM inUnwantedGroup us
g <- liftAnnex gitRepo
(files, cleanup) <- liftIO $ LsFiles.inRepo [] g
(files, cleanup) <- liftIO $ LsFiles.inRepo [] [] g
removablers <- scan unwantedrs files
void $ liftIO cleanup

View file

@ -136,7 +136,7 @@ startupScan scanner = do
-- Notice any files that were deleted before
-- watching was started.
top <- liftAnnex $ fromRepo Git.repoPath
(fs, cleanup) <- liftAnnex $ inRepo $ LsFiles.deleted [top]
(fs, cleanup) <- liftAnnex $ inRepo $ LsFiles.deleted [] [top]
forM_ fs $ \f -> do
let f' = fromRawFilePath f
liftAnnex $ onDel' f'
@ -362,7 +362,7 @@ onDel' file = do
onDelDir :: Handler
onDelDir dir _ = do
debug ["directory deleted", dir]
(fs, clean) <- liftAnnex $ inRepo $ LsFiles.deleted [toRawFilePath dir]
(fs, clean) <- liftAnnex $ inRepo $ LsFiles.deleted [] [toRawFilePath dir]
let fs' = map fromRawFilePath fs
liftAnnex $ mapM_ onDel' fs'

View file

@ -1,5 +1,12 @@
git-annex (8.20200523) UNRELEASED; urgency=medium
* Added annex.skipunknown git config, that can be set to false to change
the behavior of commands like `git annex get foo*`, to not skip
over files/dirs that are not checked into git and are explicitly listed in
the command line.
* annex.skipunknown is planned to change to default to false in a
git-annex release in early 2022. If you prefer the current behavior,
you can explicitly set it to true.
* Try to enable special remotes configured with autoenable=yes
when git-annex auto-initialization happens in a new clone of an
existing repo. Previously, git-annex init had to be explicitly run to

View file

@ -4,7 +4,7 @@
- the values a user passes to a command, and prepare actions operating
- on them.
-
- Copyright 2010-2018 Joey Hess <id@joeyh.name>
- Copyright 2010-2020 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
@ -35,13 +35,13 @@ import Annex.InodeSentinal
import qualified Database.Keys
import qualified Utility.RawFilePath as R
withFilesInGit :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesInGit a l = seekActions $ prepFiltered a $
seekHelper LsFiles.inRepo l
withFilesInGit :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesInGit ww a l = seekActions $ prepFiltered a $
seekHelper ww LsFiles.inRepo l
withFilesInGitNonRecursive :: String -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesInGitNonRecursive needforce a l = ifM (Annex.getState Annex.force)
( withFilesInGit a l
withFilesInGitNonRecursive :: WarnUnmatchWhen -> String -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesInGitNonRecursive ww needforce a l = ifM (Annex.getState Annex.force)
( withFilesInGit ww a l
, if null l
then giveup needforce
else seekActions $ prepFiltered a (getfiles [] l)
@ -49,7 +49,8 @@ withFilesInGitNonRecursive needforce a l = ifM (Annex.getState Annex.force)
where
getfiles c [] = return (reverse c)
getfiles c ((WorkTreeItem p):ps) = do
(fs, cleanup) <- inRepo $ LsFiles.inRepo [toRawFilePath p]
os <- seekOptions ww
(fs, cleanup) <- inRepo $ LsFiles.inRepo os [toRawFilePath p]
case fs of
[f] -> do
void $ liftIO $ cleanup
@ -66,7 +67,7 @@ withFilesNotInGit a l = go =<< seek
force <- Annex.getState Annex.force
g <- gitRepo
liftIO $ Git.Command.leaveZombie
<$> LsFiles.notInRepo force (map (\(WorkTreeItem f) -> toRawFilePath f) l) g
<$> LsFiles.notInRepo [] force (map (\(WorkTreeItem f) -> toRawFilePath f) l) g
go fs = seekActions $ prepFiltered a $
return $ concat $ segmentPaths (map (\(WorkTreeItem f) -> toRawFilePath f) l) fs
@ -104,7 +105,7 @@ withPairs a params = seekActions $ return $ map a $ pairs [] params
withFilesToBeCommitted :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesToBeCommitted a l = seekActions $ prepFiltered a $
seekHelper LsFiles.stagedNotDeleted l
seekHelper WarnUnmatchWorkTreeItems (const LsFiles.stagedNotDeleted) l
isOldUnlocked :: RawFilePath -> Annex Bool
isOldUnlocked f = liftIO (notSymlink f) <&&>
@ -112,12 +113,12 @@ isOldUnlocked f = liftIO (notSymlink f) <&&>
{- unlocked pointer files that are staged, and whose content has not been
- modified-}
withUnmodifiedUnlockedPointers :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withUnmodifiedUnlockedPointers a l = seekActions $
withUnmodifiedUnlockedPointers :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withUnmodifiedUnlockedPointers ww a l = seekActions $
prepFiltered a unlockedfiles
where
unlockedfiles = filterM isUnmodifiedUnlocked
=<< seekHelper LsFiles.typeChangedStaged l
=<< seekHelper ww (const LsFiles.typeChangedStaged) l
isUnmodifiedUnlocked :: RawFilePath -> Annex Bool
isUnmodifiedUnlocked f = catKeyFile f >>= \case
@ -125,9 +126,9 @@ isUnmodifiedUnlocked f = catKeyFile f >>= \case
Just k -> sameInodeCache f =<< Database.Keys.getInodeCaches k
{- Finds files that may be modified. -}
withFilesMaybeModified :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesMaybeModified a params = seekActions $
prepFiltered a $ seekHelper LsFiles.modified params
withFilesMaybeModified :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek
withFilesMaybeModified ww a params = seekActions $
prepFiltered a $ seekHelper ww LsFiles.modified params
withKeys :: (Key -> CommandSeek) -> CmdParams -> CommandSeek
withKeys a l = seekActions $ return $ map (a . parse) l
@ -228,13 +229,25 @@ prepFiltered a fs = do
seekActions :: Annex [CommandSeek] -> Annex ()
seekActions gen = sequence_ =<< gen
seekHelper :: ([RawFilePath] -> Git.Repo -> IO ([RawFilePath], IO Bool)) -> [WorkTreeItem] -> Annex [RawFilePath]
seekHelper a l = inRepo $ \g ->
seekHelper :: WarnUnmatchWhen -> ([LsFiles.Options] -> [RawFilePath] -> Git.Repo -> IO ([RawFilePath], IO Bool)) -> [WorkTreeItem] -> Annex [RawFilePath]
seekHelper ww a l = do
os <- seekOptions ww
inRepo $ \g ->
concat . concat <$> forM (segmentXargsOrdered l')
(runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) . map toRawFilePath)
(runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a os fs g) . map toRawFilePath)
where
l' = map (\(WorkTreeItem f) -> f) l
data WarnUnmatchWhen = WarnUnmatchLsFiles | WarnUnmatchWorkTreeItems
seekOptions :: WarnUnmatchWhen -> Annex [LsFiles.Options]
seekOptions WarnUnmatchLsFiles =
ifM (annexSkipUnknown <$> Annex.getGitConfig)
( return []
, return [LsFiles.ErrorUnmatch]
)
seekOptions WarnUnmatchWorkTreeItems = return []
-- An item in the work tree, which may be a file or a directory.
newtype WorkTreeItem = WorkTreeItem FilePath
@ -243,20 +256,33 @@ newtype WorkTreeItem = WorkTreeItem FilePath
-- seeking for such files.
newtype AllowHidden = AllowHidden Bool
-- Many git commands like ls-files seek work tree items matching some criteria,
-- and silently skip over anything that does not exist. But users expect
-- an error message when one of the files they provided as a command-line
-- parameter doesn't exist, so this checks that each exists.
--
-- git ls-files without --error-unmatch seeks work tree items matching
-- some criteria, and silently skips over anything that does not exist.
-- Also, when two directories are symlinked, referring to a file
-- inside the symlinked directory will be silently skipped by git commands
-- like ls-files. But, the user would be surprised for it to be skipped, so
-- check if the parent directories are symlinks.
workTreeItems :: CmdParams -> Annex [WorkTreeItem]
-- inside the symlinked directory will be silently skipped by
-- git ls-files without --error-unmatch.
--
-- Sometimes a command needs to use git-lsfiles that way, perhaps repeatedly.
-- But users expect an error message when one of the files they provided
-- as a command-line parameter doesn't exist, so this checks that each
-- exists when run with WarnUnmatchWorkTreeItems.
--
-- Note that, unlike --error-unmatch, using this does not warn
-- about command-line parameters that exist, but are not checked into git.
workTreeItems :: WarnUnmatchWhen -> CmdParams -> Annex [WorkTreeItem]
workTreeItems = workTreeItems' (AllowHidden False)
workTreeItems' :: AllowHidden -> CmdParams -> Annex [WorkTreeItem]
workTreeItems' (AllowHidden allowhidden) ps = do
workTreeItems' :: AllowHidden -> WarnUnmatchWhen -> CmdParams -> Annex [WorkTreeItem]
workTreeItems' (AllowHidden allowhidden) ww ps = do
case ww of
WarnUnmatchWorkTreeItems -> runcheck
WarnUnmatchLsFiles ->
whenM (annexSkipUnknown <$> Annex.getGitConfig)
runcheck
return (map WorkTreeItem ps)
where
runcheck = do
currbranch <- getCurrentBranch
forM_ ps $ \p -> do
relf <- liftIO $ relPathCwdToFile p
@ -265,8 +291,7 @@ workTreeItems' (AllowHidden allowhidden) ps = do
, whenM (viasymlink (upFrom relf)) $
prob (p ++ " is beyond a symbolic link")
)
return (map (WorkTreeItem) ps)
where
exists p = isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)
viasymlink Nothing = return False

View file

@ -82,10 +82,15 @@ seek o = startConcurrency commandStages $ do
giveup "--update --batch is not supported"
| otherwise -> batchFilesMatching fmt (gofile . toRawFilePath)
NoBatch -> do
l <- workTreeItems (addThese o)
let go a = a (commandAction . gofile) l
-- Avoid git ls-files complaining about files that
-- are not known to git yet, since this will add
-- them. Instead, have workTreeItems warn about other
-- problems, like files that don't exist.
let ww = WarnUnmatchWorkTreeItems
l <- workTreeItems ww (addThese o)
let go a = a ww (commandAction . gofile) l
unless (updateOnly o) $
go withFilesNotInGit
go (const withFilesNotInGit)
go withFilesMaybeModified
go withUnmodifiedUnlockedPointers

View file

@ -51,8 +51,10 @@ seek o = startConcurrency commandStages $ do
NoBatch -> withKeyOptions
(keyOptions o) (autoMode o)
(commandAction . Command.Move.startKey (fromToOptions o) Command.Move.RemoveNever)
(withFilesInGit $ commandAction . go)
=<< workTreeItems (copyFiles o)
(withFilesInGit ww $ commandAction . go)
=<< workTreeItems ww (copyFiles o)
where
ww = WarnUnmatchLsFiles
{- A copy is just a move that does not delete the source file.
- However, auto mode avoids unnecessary copies, and avoids getting or

View file

@ -57,10 +57,11 @@ seek o = startConcurrency commandStages $
Batch fmt -> batchFilesMatching fmt (go . toRawFilePath)
NoBatch -> withKeyOptions (keyOptions o) (autoMode o)
(commandAction . startKeys o)
(withFilesInGit (commandAction . go))
=<< workTreeItems (dropFiles o)
(withFilesInGit ww (commandAction . go))
=<< workTreeItems ww (dropFiles o)
where
go = whenAnnexed $ start o
ww = WarnUnmatchLsFiles
start :: DropOptions -> RawFilePath -> Key -> CommandStart
start o file key = start' o key afile ai

View file

@ -57,11 +57,12 @@ seek :: FindOptions -> CommandSeek
seek o = case batchOption o of
NoBatch -> withKeyOptions (keyOptions o) False
(commandAction . startKeys o)
(withFilesInGit (commandAction . go))
=<< workTreeItems (findThese o)
(withFilesInGit ww (commandAction . go))
=<< workTreeItems ww (findThese o)
Batch fmt -> batchFilesMatching fmt (go . toRawFilePath)
where
go = whenAnnexed $ start o
ww = WarnUnmatchLsFiles
-- only files inAnnex are shown, unless the user has requested
-- others via a limit

View file

@ -32,9 +32,11 @@ cmd = noCommit $ withGlobalOptions [annexedMatchingOptions] $
seek :: CmdParams -> CommandSeek
seek ps = unlessM crippledFileSystem $ do
withFilesInGit
withFilesInGit ww
(commandAction . (whenAnnexed $ start FixAll))
=<< workTreeItems ps
=<< workTreeItems ww ps
where
ww = WarnUnmatchLsFiles
data FixWhat = FixSymlinks | FixAll

View file

@ -94,10 +94,12 @@ seek o = startConcurrency commandStages $ do
i <- prepIncremental u (incrementalOpt o)
withKeyOptions (keyOptions o) False
(\kai -> commandAction . startKey from i kai =<< getNumCopies)
(withFilesInGit $ commandAction . (whenAnnexed (start from i)))
=<< workTreeItems (fsckFiles o)
(withFilesInGit ww $ commandAction . (whenAnnexed (start from i)))
=<< workTreeItems ww (fsckFiles o)
cleanupIncremental i
void $ tryIO $ recordActivity Fsck u
where
ww = WarnUnmatchLsFiles
checkDeadRepo :: UUID -> Annex ()
checkDeadRepo u =

View file

@ -45,8 +45,10 @@ seek o = startConcurrency downloadStages $ do
Batch fmt -> batchFilesMatching fmt (go . toRawFilePath)
NoBatch -> withKeyOptions (keyOptions o) (autoMode o)
(commandAction . startKeys from)
(withFilesInGit (commandAction . go))
=<< workTreeItems (getFiles o)
(withFilesInGit ww (commandAction . go))
=<< workTreeItems ww (getFiles o)
where
ww = WarnUnmatchLsFiles
start :: GetOptions -> Maybe Remote -> RawFilePath -> Key -> CommandStart
start o from file key = start' expensivecheck from key afile ai

View file

@ -38,9 +38,11 @@ seek o = do
| otherwise -> commandAction stop
_ -> do
let s = S.fromList ts
withFilesInGit
withFilesInGit ww
(commandAction . (whenAnnexed (start s)))
=<< workTreeItems (inprogressFiles o)
=<< workTreeItems ww (inprogressFiles o)
where
ww = WarnUnmatchLsFiles
start :: S.Set Key -> RawFilePath -> Key -> CommandStart
start s _file k

View file

@ -44,9 +44,10 @@ seek :: ListOptions -> CommandSeek
seek o = do
list <- getList o
printHeader list
withFilesInGit
(commandAction . (whenAnnexed $ start list))
=<< workTreeItems (listThese o)
withFilesInGit ww (commandAction . (whenAnnexed $ start list))
=<< workTreeItems ww (listThese o)
where
ww = WarnUnmatchLsFiles
getList :: ListOptions -> Annex [(UUID, RemoteName, TrustLevel)]
getList o

View file

@ -30,8 +30,10 @@ cmd = withGlobalOptions [jsonOptions, annexedMatchingOptions] $
seek :: CmdParams -> CommandSeek
seek ps = do
l <- workTreeItems ps
withFilesInGit (commandAction . (whenAnnexed startNew)) l
l <- workTreeItems ww ps
withFilesInGit ww (commandAction . (whenAnnexed startNew)) l
where
ww = WarnUnmatchLsFiles
startNew :: RawFilePath -> Key -> CommandStart
startNew file key = ifM (isJust <$> isAnnexLink file)

View file

@ -86,11 +86,13 @@ seek o = do
zone <- liftIO getCurrentTimeZone
let outputter = mkOutputter m zone o
case (logFiles o, allOption o) of
(fs, False) -> withFilesInGit
(fs, False) -> withFilesInGit ww
(commandAction . (whenAnnexed $ start o outputter))
=<< workTreeItems fs
=<< workTreeItems ww fs
([], True) -> commandAction (startAll o outputter)
(_, True) -> giveup "Cannot specify both files and --all"
where
ww = WarnUnmatchLsFiles
start :: LogOptions -> (FilePath -> Outputter) -> RawFilePath -> Key -> CommandStart
start o outputter file key = do

View file

@ -31,7 +31,7 @@ run _ file = seekSingleGitFile file >>= \case
-- But, this plumbing command does not recurse through directories.
seekSingleGitFile :: FilePath -> Annex (Maybe RawFilePath)
seekSingleGitFile file = do
(l, cleanup) <- inRepo (Git.LsFiles.inRepo [toRawFilePath file])
(l, cleanup) <- inRepo (Git.LsFiles.inRepo [] [toRawFilePath file])
r <- case l of
(f:[]) | takeFileName (fromRawFilePath f) == takeFileName file ->
return (Just f)

View file

@ -75,15 +75,16 @@ seek :: MetaDataOptions -> CommandSeek
seek o = case batchOption o of
NoBatch -> do
c <- liftIO currentVectorClock
let ww = WarnUnmatchLsFiles
let seeker = case getSet o of
Get _ -> withFilesInGit
GetAll -> withFilesInGit
Set _ -> withFilesInGitNonRecursive
Get _ -> withFilesInGit ww
GetAll -> withFilesInGit ww
Set _ -> withFilesInGitNonRecursive ww
"Not recursively setting metadata. Use --force to do that."
withKeyOptions (keyOptions o) False
(commandAction . startKeys c o)
(seeker (commandAction . (whenAnnexed (start c o))))
=<< workTreeItems (forFiles o)
=<< workTreeItems ww (forFiles o)
Batch fmt -> withMessageState $ \s -> case outputType s of
JSONOutput _ -> ifM limited
( giveup "combining --batch with file matching options is not currently supported"

View file

@ -26,7 +26,10 @@ cmd = withGlobalOptions [annexedMatchingOptions] $
paramPaths (withParams seek)
seek :: CmdParams -> CommandSeek
seek = withFilesInGit (commandAction . (whenAnnexed start)) <=< workTreeItems
seek = withFilesInGit ww (commandAction . (whenAnnexed start))
<=< workTreeItems ww
where
ww = WarnUnmatchLsFiles
start :: RawFilePath -> Key -> CommandStart
start file key = do

View file

@ -44,12 +44,13 @@ seek :: MirrorOptions -> CommandSeek
seek o = startConcurrency stages $
withKeyOptions (keyOptions o) False
(commandAction . startKey o (AssociatedFile Nothing))
(withFilesInGit (commandAction . (whenAnnexed $ start o)))
=<< workTreeItems (mirrorFiles o)
(withFilesInGit ww (commandAction . (whenAnnexed $ start o)))
=<< workTreeItems ww (mirrorFiles o)
where
stages = case fromToOptions o of
FromRemote _ -> downloadStages
ToRemote _ -> commandStages
ww = WarnUnmatchLsFiles
start :: MirrorOptions -> RawFilePath -> Key -> CommandStart
start o file k = startKey o afile (k, ai)

View file

@ -60,13 +60,14 @@ seek o = startConcurrency stages $ do
Batch fmt -> batchFilesMatching fmt (go . toRawFilePath)
NoBatch -> withKeyOptions (keyOptions o) False
(commandAction . startKey (fromToOptions o) (removeWhen o))
(withFilesInGit (commandAction . go))
=<< workTreeItems (moveFiles o)
(withFilesInGit ww (commandAction . go))
=<< workTreeItems ww (moveFiles o)
where
stages = case fromToOptions o of
Right (FromRemote _) -> downloadStages
Right (ToRemote _) -> commandStages
Left ToHere -> downloadStages
ww = WarnUnmatchLsFiles
start :: FromToHereOptions -> RemoveWhen -> RawFilePath -> Key -> CommandStart
start fromto removewhen f k = start' fromto removewhen afile k ai

View file

@ -129,7 +129,9 @@ send ups fs = do
-- expensive.
starting "sending files" (ActionItemOther Nothing) $
withTmpFile "send" $ \t h -> do
fs' <- seekHelper LsFiles.inRepo =<< workTreeItems fs
let ww = WarnUnmatchLsFiles
fs' <- seekHelper ww LsFiles.inRepo
=<< workTreeItems ww fs
matcher <- Limit.getMatcher
let addlist f o = whenM (matcher $ MatchingFile $ FileInfo f f) $
liftIO $ hPutStrLn h o

View file

@ -32,7 +32,8 @@ cmd = command "pre-commit" SectionPlumbing
seek :: CmdParams -> CommandSeek
seek ps = do
l <- workTreeItems ps
let ww = WarnUnmatchWorkTreeItems
l <- workTreeItems ww ps
-- fix symlinks to files being committed
flip withFilesToBeCommitted l $ \f -> commandAction $
maybe stop (Command.Fix.start Command.Fix.FixSymlinks f)

View file

@ -636,11 +636,11 @@ seekSyncContent o rs currbranch = do
Just WantAllKeys -> Just <$> genBloomFilter (seekworktree mvar [])
_ -> case currbranch of
(Just origbranch, Just adj) | adjustmentHidesFiles adj -> do
l <- workTreeItems' (AllowHidden True) (contentOfOption o)
l <- workTreeItems' (AllowHidden True) ww (contentOfOption o)
seekincludinghidden origbranch mvar l (const noop)
pure Nothing
_ -> do
l <- workTreeItems (contentOfOption o)
l <- workTreeItems ww (contentOfOption o)
seekworktree mvar l (const noop)
pure Nothing
withKeyOptions' (keyOptions o) False
@ -651,13 +651,15 @@ seekSyncContent o rs currbranch = do
liftIO $ not <$> isEmptyMVar mvar
where
seekworktree mvar l bloomfeeder =
seekHelper LsFiles.inRepo l
seekHelper ww LsFiles.inRepo l
>>= gofiles bloomfeeder mvar
seekincludinghidden origbranch mvar l bloomfeeder =
seekHelper (LsFiles.inRepoOrBranch origbranch) l
seekHelper ww (LsFiles.inRepoOrBranch origbranch) l
>>= gofiles bloomfeeder mvar
ww = WarnUnmatchLsFiles
gofiles bloomfeeder mvar = mapM_ $ \f ->
ifAnnexed f
(go (Right bloomfeeder) mvar (AssociatedFile (Just f)))

View file

@ -23,7 +23,10 @@ cmd = withGlobalOptions [annexedMatchingOptions] $
paramPaths (withParams seek)
seek :: CmdParams -> CommandSeek
seek ps = (withFilesInGit $ commandAction . whenAnnexed start) =<< workTreeItems ps
seek ps = (withFilesInGit ww $ commandAction . whenAnnexed start)
=<< workTreeItems ww ps
where
ww = WarnUnmatchLsFiles
start :: RawFilePath -> Key -> CommandStart
start file key = stopUnless (inAnnex key) $

View file

@ -27,7 +27,7 @@ seek :: CmdParams -> CommandSeek
seek ps = do
-- Safety first; avoid any undo that would touch files that are not
-- in the index.
(fs, cleanup) <- inRepo $ LsFiles.notInRepo False (map toRawFilePath ps)
(fs, cleanup) <- inRepo $ LsFiles.notInRepo [] False (map toRawFilePath ps)
unless (null fs) $
giveup $ "Cannot undo changes to files that are not checked into git: " ++ unwords (map fromRawFilePath fs)
void $ liftIO $ cleanup

View file

@ -41,11 +41,13 @@ check = do
seek :: CmdParams -> CommandSeek
seek ps = do
l <- workTreeItems ps
l <- workTreeItems ww ps
withFilesNotInGit (commandAction . whenAnnexed (startCheckIncomplete . fromRawFilePath)) l
Annex.changeState $ \s -> s { Annex.fast = True }
withFilesInGit (commandAction . whenAnnexed Command.Unannex.start) l
withFilesInGit ww (commandAction . whenAnnexed Command.Unannex.start) l
finish
where
ww = WarnUnmatchLsFiles
{- git annex symlinks that are not checked into git could be left by an
- interrupted add. -}

View file

@ -27,7 +27,10 @@ mkcmd n d = withGlobalOptions [jsonOptions, annexedMatchingOptions] $
command n SectionCommon d paramPaths (withParams seek)
seek :: CmdParams -> CommandSeek
seek ps = withFilesInGit (commandAction . whenAnnexed start) =<< workTreeItems ps
seek ps = withFilesInGit ww (commandAction . whenAnnexed start)
=<< workTreeItems ww ps
where
ww = WarnUnmatchLsFiles
start :: RawFilePath -> Key -> CommandStart
start file key = ifM (isJust <$> isAnnexLink file)

View file

@ -210,9 +210,9 @@ withKeysReferenced' mdir initial a = do
( return ([], return True)
, do
top <- fromRepo Git.repoPath
inRepo $ LsFiles.allFiles [top]
inRepo $ LsFiles.allFiles [] [top]
)
Just dir -> inRepo $ LsFiles.inRepo [toRawFilePath dir]
Just dir -> inRepo $ LsFiles.inRepo [] [toRawFilePath dir]
go v [] = return v
go v (f:fs) = do
mk <- lookupFile f

View file

@ -101,7 +101,7 @@ checkoutViewBranch view mkbranch = do
- removed.) -}
top <- liftIO . absPath . fromRawFilePath =<< fromRepo Git.repoPath
(l, cleanup) <- inRepo $
LsFiles.notInRepoIncludingEmptyDirectories False
LsFiles.notInRepoIncludingEmptyDirectories [] False
[toRawFilePath top]
forM_ l (removeemptydir top)
liftIO $ void cleanup

View file

@ -57,8 +57,10 @@ seek o = do
NoBatch ->
withKeyOptions (keyOptions o) False
(commandAction . startKeys o m)
(withFilesInGit (commandAction . go))
=<< workTreeItems (whereisFiles o)
(withFilesInGit ww (commandAction . go))
=<< workTreeItems ww (whereisFiles o)
where
ww = WarnUnmatchLsFiles
start :: WhereisOptions -> M.Map UUID Remote -> RawFilePath -> Key -> CommandStart
start o remotemap file key =

View file

@ -1,11 +1,12 @@
{- git ls-files interface
-
- Copyright 2010-2019 Joey Hess <id@joeyh.name>
- Copyright 2010-2020 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
module Git.LsFiles (
Options(..),
inRepo,
inRepoOrBranch,
notInRepo,
@ -13,10 +14,8 @@ module Git.LsFiles (
allFiles,
deleted,
modified,
modifiedOthers,
staged,
stagedNotDeleted,
stagedOthersDetails,
stagedDetails,
typeChanged,
typeChangedStaged,
@ -63,101 +62,63 @@ guardSafeForLsFiles r a
| safeForLsFiles r = a
| otherwise = error $ "git ls-files is unsafe to run on repository " ++ repoDescribe r
data Options = ErrorUnmatch
{- Lists files that are checked into git's index at the specified paths.
- With no paths, all files are listed.
-}
inRepo :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
inRepo = inRepo' []
inRepo :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
inRepo = inRepo' [Param "--cached"]
inRepo' :: [CommandParam] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
inRepo' ps l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo
inRepo' :: [CommandParam] -> [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
inRepo' ps os l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo
where
params =
Param "ls-files" :
Param "--cached" :
Param "-z" :
ps ++
map op os ++ ps ++
(Param "--" : map (File . fromRawFilePath) l)
op ErrorUnmatch = Param "--error-unmatch"
{- Files that are checked into the index or have been committed to a
- branch. -}
inRepoOrBranch :: Branch -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
inRepoOrBranch b = inRepo' [Param $ "--with-tree=" ++ fromRef b]
inRepoOrBranch :: Branch -> [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
inRepoOrBranch b = inRepo'
[ Param "--cached"
, Param ("--with-tree=" ++ fromRef b)
]
{- Scans for files at the specified locations that are not checked into git. -}
notInRepo :: Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
notInRepo :: [Options] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
notInRepo = notInRepo' []
notInRepo' :: [CommandParam] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
notInRepo' ps include_ignored l repo = guardSafeForLsFiles repo $
pipeNullSplit' params repo
notInRepo' :: [CommandParam] -> [Options] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
notInRepo' ps os include_ignored =
inRepo' (Param "--others" : ps ++ exclude) os
where
params = concat
[ [ Param "ls-files", Param "--others"]
, ps
, exclude
, [ Param "-z", Param "--" ]
, map (File . fromRawFilePath) l
]
exclude
| include_ignored = []
| otherwise = [Param "--exclude-standard"]
{- Scans for files at the specified locations that are not checked into
- git. Empty directories are included in the result. -}
notInRepoIncludingEmptyDirectories :: Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
notInRepoIncludingEmptyDirectories :: [Options] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
notInRepoIncludingEmptyDirectories = notInRepo' [Param "--directory"]
{- Finds all files in the specified locations, whether checked into git or
- not. -}
allFiles :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
allFiles l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo
where
params =
Param "ls-files" :
Param "--cached" :
Param "--others" :
Param "-z" :
Param "--" :
map (File . fromRawFilePath) l
allFiles :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
allFiles = inRepo' [Param "--cached", Param "--others"]
{- Returns a list of files in the specified locations that have been
- deleted. -}
deleted :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
deleted l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo
where
params =
Param "ls-files" :
Param "--deleted" :
Param "-z" :
Param "--" :
map (File . fromRawFilePath) l
deleted :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
deleted = inRepo' [Param "--deleted"]
{- Returns a list of files in the specified locations that have been
- modified. -}
modified :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
modified l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo
where
params =
Param "ls-files" :
Param "--modified" :
Param "-z" :
Param "--" :
map (File . fromRawFilePath) l
{- Files that have been modified or are not checked into git (and are not
- ignored). -}
modifiedOthers :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
modifiedOthers l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo
where
params =
Param "ls-files" :
Param "--modified" :
Param "--others" :
Param "--exclude-standard" :
Param "-z" :
Param "--" :
map (File . fromRawFilePath) l
modified :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
modified = inRepo' [Param "--modified"]
{- Returns a list of all files that are staged for commit. -}
staged :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)
@ -177,11 +138,6 @@ staged' ps l repo = guardSafeForLsFiles repo $
type StagedDetails = (RawFilePath, Maybe Sha, Maybe FileMode)
{- Returns details about files that are staged in the index,
- as well as files not yet in git. Skips ignored files. -}
stagedOthersDetails :: [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool)
stagedOthersDetails = stagedDetails' [Param "--others", Param "--exclude-standard"]
{- Returns details about all files that are staged in the index. -}
stagedDetails :: [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool)
stagedDetails = stagedDetails' []

11
NEWS
View file

@ -1,3 +1,14 @@
git-annex (8.20200523) UNRELEASED; urgency=medium
Transition notice: Commands like `git-annex get foo*` silently skip over
files that are not checked into git. Since that can be surprising in many
cases, the behavior will change to erroring out when one of the listed
files is not checked into git. This change is planned for a git-annex
release in early 2022. If you would like to keep the current
behavior, use git config to set annex.skipunknown to true.
-- Joey Hess <id@joeyh.name> Thu, 28 May 2020 13:23:40 -0400
git-annex (8.20200226) upstream; urgency=medium
This version of git-annex uses repository version 8 for all repositories.

View file

@ -123,6 +123,7 @@ data GitConfig = GitConfig
, annexCacheCreds :: Bool
, annexAutoUpgradeRepository :: Bool
, annexCommitMode :: CommitMode
, annexSkipUnknown :: Bool
, coreSymlinks :: Bool
, coreSharedRepository :: SharedRepository
, receiveDenyCurrentBranch :: DenyCurrentBranch
@ -214,6 +215,7 @@ extractGitConfig configsource r = GitConfig
, annexCommitMode = if getbool (annexConfig "allowsign") False
then ManualCommit
else AutomaticCommit
, annexSkipUnknown = getbool (annexConfig "skipunknown") True
, coreSymlinks = getbool "core.symlinks" True
, coreSharedRepository = getSharedRepository r
, receiveDenyCurrentBranch = getDenyCurrentBranch r

View file

@ -85,7 +85,7 @@ updateSymlinks :: Annex ()
updateSymlinks = do
showAction "updating symlinks"
top <- fromRepo Git.repoPath
(files, cleanup) <- inRepo $ LsFiles.inRepo [top]
(files, cleanup) <- inRepo $ LsFiles.inRepo [] [top]
forM_ files (fixlink . fromRawFilePath)
void $ liftIO cleanup
where

View file

@ -35,3 +35,12 @@ P.S. It might be a related observation that git-annex metadata does exit with n
[[!tag projects/datalad]]
[[!meta title="silently skipping files that are not in git or not annexed is sometimes surprising to some"]]
> annex.skipunknown false will make git-annex error out in this situation.
> That will become the default in a couple of years, but can be set already
> by those who don't like the behavior of skipping.
>
> In the case of addurl --batch though, do see my first comment for a way to
> avoid any errors.
>
> [[done]] --[[Joey]]

View file

@ -27,7 +27,8 @@ temp file that is not in git, then they would have to change scripts
and workflows.
Implementing it may be as simple as passing --error-unmatch to git
ls-files.
ls-files. (And disable git-annex's code that checks for parameters that are
not existing files.)
It could be an option, but I don't really consider an option as fixing the
surprising behavior. And once you know git-annex behaves this way, I think

View file

@ -0,0 +1,17 @@
[[!comment format=mdwn
username="joey"
subject="""comment 3"""
date="2020-05-28T19:49:58Z"
content="""
Implemented annex.skipunknown git config, that will make it error out when
given a file that git doesn't know about.
Not default yet, will be in a couple of years.
[[todo/complete_annex.skipunknown_transition_in_2022]]
As to git-annex skipping non-annexed files, I'm leaning toward keeping it
the way it is, and it's not really the subject of this bug report, except
maybe that it's not entirely consistent with the annex.skipunknown
behavior for non-git files. If users complain about it, I'll consider it
again.
"""]]

View file

@ -51,3 +51,7 @@ It would be nice if git-annex could give an error to explain why the unlock fail
### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
git-annex is amazing, I use it all the time. Thanks!
> annex.skipunknown false will make git-annex error out in this situation.
> That will become the default in a couple of years, but can be set already
> by those who don't like the behavior of skipping. [[done]] --[[Joey]]

View file

@ -880,6 +880,28 @@ Like other git commands, git-annex is configured via `.git/config`.
The default reserve is 1 megabyte.
* `annex.skipunknown`
Set to true to make commands like "git-annex get" silently skip over
items that are listed in the command line, but are not checked into git.
Set to false to make it an error for commands like "git-annex get"
to be asked to operate on files that are not checked into git.
The default is currently true, but is planned to change to false in a
release in 2022.
Note that, when annex.skipunknown is false, a command like "git-annex get ."
will fail if no files in the current directory are checked into git,
but a command like "git-annex get" will not fail, because the current
directory is not listed, but is implicit. Commands like "git-annex get foo/"
will fail if no files in the directory are checked into git, but if
at least one file is, it will ignore other files that are not. This is
all the same as the behavior of "git-ls files --error-unmatch".
Also note that git-annex skips files that are checked into git, but are
not annexed files, this setting does not affect that.
* `annex.largefiles`
Used to configure which files are large enough to be added to the annex.

View file

@ -0,0 +1,4 @@
annex.skipunknown should be changed to default to false in early 2022.
This transition was started in mid 2020 and there should have been plenty
of time for people to learn about it and either set their preferred
behavior or change workflows for the new behavior.