* assistant --autostop: Avoid crashing when ~/.config/git-annex/autostart
lists a directory that it cannot chdir to.
* Honor --force option when operating on a local git remote.
+ * When a nonexistant file is passed to a command and
+ --json-error-messages is enabled, output a JSON object indicating the
+ problem. (But git ls-files --error-unmatch still displays errors about
+ such files in some situations.)
-- Joey Hess <id@joeyh.name> Sat, 08 Apr 2023 13:57:18 -0400
pairs c (x:y:xs) = pairs ((x,y):c) xs
pairs _ _ = giveup "expected pairs"
-withFilesToBeCommitted :: ((SeekInput, RawFilePath) -> CommandSeek) -> WorkTreeItems -> CommandSeek
-withFilesToBeCommitted a l = seekFiltered (const (pure True)) a $
- seekHelper id WarnUnmatchWorkTreeItems (const LsFiles.stagedNotDeleted) l
+withFilesToBeCommitted :: WarnUnmatchWhen -> ((SeekInput, RawFilePath) -> CommandSeek) -> WorkTreeItems -> CommandSeek
+withFilesToBeCommitted ww a l = seekFiltered (const (pure True)) a $
+ seekHelper id ww (const LsFiles.stagedNotDeleted) l
{- unlocked pointer files that are staged, and whose content has not been
- modified-}
and <$> sequence cleanups
seekHelper _ _ _ NoWorkTreeItems = return ([], pure True)
-data WarnUnmatchWhen = WarnUnmatchLsFiles | WarnUnmatchWorkTreeItems
+data WarnUnmatchWhen = WarnUnmatchLsFiles String | WarnUnmatchWorkTreeItems String
seekOptions :: WarnUnmatchWhen -> Annex [LsFiles.Options]
-seekOptions WarnUnmatchLsFiles =
+seekOptions (WarnUnmatchLsFiles _) =
ifM (annexSkipUnknown <$> Annex.getGitConfig)
( return []
, return [LsFiles.ErrorUnmatch]
)
-seekOptions WarnUnmatchWorkTreeItems = return []
+seekOptions (WarnUnmatchWorkTreeItems _) = return []
-- Items in the work tree, which may be files or directories.
data WorkTreeItems
workTreeItems' :: AllowHidden -> WarnUnmatchWhen -> CmdParams -> Annex WorkTreeItems
workTreeItems' (AllowHidden allowhidden) ww ps = case ww of
- WarnUnmatchWorkTreeItems -> runcheck
- WarnUnmatchLsFiles ->
+ (WarnUnmatchWorkTreeItems action) -> runcheck action
+ (WarnUnmatchLsFiles action) ->
ifM (annexSkipUnknown <$> Annex.getGitConfig)
- ( runcheck
+ ( runcheck action
, return $ WorkTreeItems ps
)
where
- runcheck = do
+ runcheck action = do
currbranch <- getCurrentBranch
stopattop <- prepviasymlink
ps' <- flip filterM ps $ \p -> do
let p' = toRawFilePath p
relf <- liftIO $ relPathCwdToFile p'
ifM (not <$> (exists p' <||> hidden currbranch relf))
- ( prob (QuotedPath (toRawFilePath p) <> " not found")
+ ( prob action FileNotFound p' "not found"
, ifM (viasymlink stopattop (upFrom relf))
- ( prob (QuotedPath (toRawFilePath p) <> " is beyond a symbolic link")
+ ( prob action FileBeyondSymbolicLink p' "is beyond a symbolic link"
, return True
)
)
<$> catObjectMetaDataHidden f currbranch
| otherwise = return False
- prob msg = do
- toplevelWarning False msg
+ prob action errorid p msg = do
+ toplevelFileProblem False errorid msg action p Nothing (SeekInput [fromRawFilePath p])
Annex.incError
return False
-- 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
+ let ww = WarnUnmatchWorkTreeItems "add"
l <- workTreeItems ww (addThese o)
let go b a = a ww (commandAction . gofile b) l
unless (updateOnly o) $
Batch fmt -> batchOnly (keyOptions o) (copyFiles o) $
batchAnnexed fmt seeker keyaction
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "copy"
seeker = AnnexedFileSeeker
{ startAction = start o fto
Batch fmt -> batchOnly (keyOptions o) (dropFiles o) $
batchAnnexed fmt seeker (startKeys o from)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "drop"
start :: DropOptions -> Maybe Remote -> SeekInput -> RawFilePath -> Key -> CommandStart
start o from si file key = start' o from key afile ai si
c <- inRepo $ Git.commitTree cmode cmessage [] t
liftIO $ putStrLn (fromRef c)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "filter-branch"
Batch fmt -> batchOnly (keyOptions o) (findThese o) $
batchAnnexedFiles fmt seeker
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "find"
-- Default to needing content to be present, but if the user specified a
-- limit, content does not need to be present.
seek ps = unlessM crippledFileSystem $
withFilesInGitAnnex ww seeker =<< workTreeItems ww ps
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "fix"
seeker = AnnexedFileSeeker
{ startAction = start FixAll
, checkContentPresent = Nothing
cleanupIncremental i
void $ tryIO $ recordActivity Fsck u
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "fsck"
checkDeadRepo :: UUID -> Annex ()
checkDeadRepo u =
Batch fmt -> batchOnly (keyOptions o) (getFiles o) $
batchAnnexed fmt seeker (startKeys from)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "get"
start :: GetOptions -> Maybe Remote -> SeekInput -> RawFilePath -> Key -> CommandStart
start o from si file key = start' expensivecheck from key afile ai si
withFilesInGitAnnex ww seeker
=<< workTreeItems ww (inprogressFiles o)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "inprogress"
start :: IsTerminal -> S.Set Key -> SeekInput -> RawFilePath -> Key -> CommandStart
start isterminal s _si _file k
}
withFilesInGitAnnex ww seeker =<< workTreeItems ww (listThese o)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "list"
getList :: ListOptions -> Annex [(UUID, RemoteName, TrustLevel)]
getList o
seek :: CmdParams -> CommandSeek
seek ps = withFilesInGitAnnex ww seeker =<< workTreeItems ww ps
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "lock"
seeker = AnnexedFileSeeker
{ startAction = start
, checkContentPresent = Nothing
, giveup "This repository is read-only, and there are unmerged git-annex branches, which prevents displaying location log changes. (Set annex.merge-annex-branches to false to ignore the unmerged git-annex branches.)"
)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "log"
start :: LogOptions -> (FilePath -> Outputter) -> SeekInput -> RawFilePath -> Key -> CommandStart
start o outputter _ file key = do
seek o = case batchOption o of
NoBatch -> do
c <- currentVectorClock
- let ww = WarnUnmatchLsFiles
+ let ww = WarnUnmatchLsFiles "metadata"
let seeker = AnnexedFileSeeker
{ startAction = start c o
, checkContentPresent = Nothing
seek :: MigrateOptions -> CommandSeek
seek o = withFilesInGitAnnex ww seeker =<< workTreeItems ww (migrateThese o)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "migrate"
seeker = AnnexedFileSeeker
{ startAction = start o
, checkContentPresent = Nothing
stages = case fromToOptions o of
FromRemote _ -> transferStages
ToRemote _ -> commandStages
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "mirror"
seeker = AnnexedFileSeeker
{ startAction = start o
, checkContentPresent = Nothing
, usesLocationLog = True
}
keyaction = startKey fto (removeWhen o)
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "move"
stages :: FromToHereOptions -> UsedStages
stages (FromOrToRemote (FromRemote _)) = transferStages
-- expensive.
starting "sending files" (ActionItemOther Nothing) (SeekInput []) $
withTmpFile "send" $ \t h -> do
- let ww = WarnUnmatchLsFiles
+ let ww = WarnUnmatchLsFiles "multicast"
(fs', cleanup) <- seekHelper id ww LsFiles.inRepo
=<< workTreeItems ww fs
matcher <- Limit.getMatcher
seek :: CmdParams -> CommandSeek
seek ps = do
- let ww = WarnUnmatchWorkTreeItems
+ let ww = WarnUnmatchWorkTreeItems "pre-commit"
l <- workTreeItems ww ps
-- fix symlinks to files being committed
- flip withFilesToBeCommitted l $ \(si, f) -> commandAction $
+ flip (withFilesToBeCommitted ww) l $ \(si, f) -> commandAction $
maybe stop (Command.Fix.start Command.Fix.FixSymlinks si f)
=<< isAnnexLink f
-- after a merge conflict or git cherry-pick or stash, pointer
in seekFiltered (const (pure True)) filterer $
seekHelper id ww (LsFiles.inRepoOrBranch origbranch) l
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "sync"
gofile bloom mvar _ f k =
go (Right bloom) mvar (AssociatedFile (Just f)) k
seek :: CmdParams -> CommandSeek
seek ps = withFilesInGitAnnex ww (seeker False) =<< workTreeItems ww ps
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "unannex"
seeker :: Bool -> AnnexedFileSeeker
seeker fast = AnnexedFileSeeker
l <- workTreeItems ww ps
withFilesNotInGit
(CheckGitIgnore False)
- WarnUnmatchWorkTreeItems
+ (WarnUnmatchWorkTreeItems "uninit")
checksymlinks
l
withFilesInGitAnnex ww (Command.Unannex.seeker True) l
finish
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "uninit"
checksymlinks (_, f) =
commandAction $ lookupKey f >>= \case
Nothing -> stop
seek :: CmdParams -> CommandSeek
seek ps = withFilesInGitAnnex ww seeker =<< workTreeItems ww ps
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "unlock"
seeker = AnnexedFileSeeker
{ startAction = start
, checkContentPresent = Nothing
Batch fmt -> batchOnly (keyOptions o) (whereisFiles o) $
batchAnnexed fmt seeker (startKeys o m)
where
- ww = WarnUnmatchLsFiles
+ ww = WarnUnmatchLsFiles "whereis"
start :: WhereisOptions -> M.Map UUID Remote -> SeekInput -> RawFilePath -> Key -> CommandStart
start o remotemap si file key =
showEndFail,
showEndResult,
endResult,
+ ErrorId(..),
+ toplevelFileProblem,
toplevelWarning,
warning,
earlyWarning,
indent,
JSON.JSONChunk(..),
maybeShowJSON,
+ maybeShowJSON',
showFullJSON,
showCustom,
showHeader,
endResult True = "ok"
endResult False = "failed"
+toplevelMsg :: StringContainingQuotedPath -> StringContainingQuotedPath
+toplevelMsg = ("git-annex: " <>)
+
+toplevelFileProblem :: Bool -> ErrorId -> StringContainingQuotedPath -> String -> RawFilePath -> Maybe Key -> SeekInput -> Annex ()
+toplevelFileProblem makeway errorid msg action file mkey si = do
+ maybeShowJSON' $ JSON.start action (Just file) mkey si
+ maybeShowJSON' $ JSON.errorid errorid
+ warning' makeway id (toplevelMsg (QuotedPath file <> " " <> msg))
+ maybeShowJSON' $ JSON.end False
+
toplevelWarning :: Bool -> StringContainingQuotedPath -> Annex ()
-toplevelWarning makeway s = warning' makeway id ("git-annex: " <> s)
+toplevelWarning makeway s = warning' makeway id (toplevelMsg s)
warning :: StringContainingQuotedPath -> Annex ()
warning = warning' True indent
earlyWarning = warning' False id
warning' :: Bool -> (S.ByteString -> S.ByteString) -> StringContainingQuotedPath -> Annex ()
-warning' makeway consolewhitespacef w = do
+warning' makeway consolewhitespacef msg = do
when makeway $
outputMessage JSON.none id "\n"
- outputError (\s -> consolewhitespacef s <> "\n") w
+ outputError (\s -> consolewhitespacef s <> "\n") msg
{- Not concurrent output safe. -}
warningIO :: String -> IO ()
maybeShowJSON :: JSON.JSONChunk v -> Annex ()
maybeShowJSON v = void $ withMessageState $ bufferJSON (JSON.add v)
+maybeShowJSON' :: JSON.JSONBuilder -> Annex ()
+maybeShowJSON' v = void $ withMessageState $ bufferJSON v
+
{- Shows a complete JSON value, only when in json mode. -}
showFullJSON :: JSON.JSONChunk v -> Annex Bool
showFullJSON v = withMessageState $ bufferJSON (JSON.complete v)
addErrorMessage,
note,
info,
+ errorid,
add,
complete,
progress,
import Utility.Percentage
import Utility.Aeson
import Utility.FileSystemEncoding
+import Types.Messages
-- A global lock to avoid concurrent threads emitting json at the same time.
{-# NOINLINE emitLock #-}
putMVar emitLock ()
-- Building up a JSON object can be done by first using start,
--- then add and note any number of times, and finally complete.
+-- then add and note and errorid any number of times, and finally
+-- complete.
type JSONBuilder = Maybe (Object, Bool) -> Maybe (Object, Bool)
none :: JSONBuilder
String (old <> "\n" <> new)
combinelines new _old = new
+errorid :: ErrorId -> JSONBuilder
+errorid _ Nothing = Nothing
+errorid eid (Just (o, e)) = Just (HM.unionWith replaceold (HM.singleton "errorid" (toJSON' (show eid))) o, e)
+ where
+ replaceold new _old = new
+
info :: String -> JSONBuilder
info s _ = case j of
Object o -> Just (o, True)
data SerializedOutputResponse
= ReadyPrompt
deriving (Eq, Show)
+
+-- | Error identifiers. Avoid changing these.
+data ErrorId
+ = FileNotFound
+ | FileBeyondSymbolicLink
+ deriving (Show)
--- /dev/null
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 8"""
+ date="2023-04-25T22:45:24Z"
+ content="""
+Ok, implemented the simple alternative. Here's how it looks:
+
+ joey@darkstar:~/tmp/xxx>git-annex add 'dne' --json --json-error-messages
+ {"command":"add","error-messages":["git-annex: dne not found"],"errorid":"FileNotFound","file":"dne","input":["dne"],"success":false}
+ add: 1 failed
+
+The errorid will remain stable. I can add those to other error messages
+now, on request BTW.
+
+Note that when git-annex relies on `git ls-files --error-unmatch` to
+complain about nonexistant or non-git files, the error messages from
+git will still be displayed to stderr, not this nice json. So
+datalad will need to keep its parser for that part.
+"""]]