Как использовать библиотеку darcs для запроса информации о патчах?
Я хочу написать программу Haskell, которая запрашивает информацию о репозитории darcs. Вместо того, чтобы вызывать исполняемые darcs и анализировать результаты, я предпочел бы использовать библиотеку darcs напрямую. сказал, что "очень много работает" и "не имеет стабильного API" , но кажется полезным.
Думаю, я мог бы ответить на мои вопросы, изучив исходный код darcsden, например, начиная с этого модуля, но я думаю, что это может быть полезно не только для меня, если кто-то знающий предоставит прокомментированное введение для дополнения такого исследования.
Итак, вот конкретный пример.
Как я могу вычислить для данного файла самый последний патч, который затронул его вместе с датой, автором и именем патча? Было бы полезно, если вы объясните функции библиотеки ключей, используемые в вашем решении.
Edit:
Вот некоторые замечания, которые могут быть неочевидны для кого-то, незнакомого с исходным кодом darcs. Я узнал их из Джейсон Дагит, главный тезис и надеюсь, что они будут полезны для понимания ответа, данного Ганесом.
В Darcs патчи имеют предварительный и постконтент, представляющий состояние хранилища до и после применения патча. В исходном коде эти типы моделируются с использованием типов phantom типа патчей. Эти типы phantom называются свидетелями, а seal2
используется для их устранения.
В списках патчей в типе представлен только первый предварительный контекст и последний пост-контекст. Все остальные контексты скрыты с использованием экзистенциальных типов. Darcs определяет форвардные списки (называемые FL) и обратные списки (называемые RL). Обратные списки хранят патчи в обратном (хронологическом) порядке (по модулю исправления патча, выполняемого darcs). Обратные списки могут использоваться для доступа к последнему патчу в позиции головы. Все функции с RL в их имени создают или работают с такими обратными списками.
Ответы
Ответ 1
-- This works with darcs 2.9.5 (a tag in the development repo
-- at http://darcs.net/screened).
--
-- It should work with darcs 2.8.2 with the following changes:
-- - some minor namespace changes
-- - change withRepositoryDirectory to pass [] instead of YesUseCache
-- - comment out the line below that uses the "patch index"
import Control.Applicative ( (<$>) )
import Darcs.Patch.Info ( PatchInfo )
import Darcs.Patch.Inspect ( listTouchedFiles )
import Darcs.Patch.PatchInfoAnd ( info )
import Darcs.Patch.Set ( newset2RL )
import Darcs.Patch.Witnesses.Ordered ( mapRL )
import Darcs.Patch.Witnesses.Sealed ( seal2, unseal2 )
import Darcs.Repository
( withRepositoryDirectory, RepoJob(..), readRepo )
import Darcs.Repository.FileMod ( filterPatches )
import Darcs.Repository.Flags ( UseCache(..) )
import Data.Maybe ( listToMaybe )
getChange
:: FilePath -- ^repository directory
-> FilePath -- ^file path
-> IO (Maybe PatchInfo) -- ^patch metadata
getChange repoDir fileName =
-- Select the repository from repositoryDirectory.
--
-- The function parameter to 'RepoJob' needs to be polymorphic
-- in the underlying patch type (darcs-1 or darcs-2).
withRepositoryDirectory YesUseCache repoDir $ RepoJob $ \repo -> do
-- 'readRepo' gives us a PatchSet, a lazy witnessed list of all
-- the patches structured by "clean tags".
--
-- We use 'newset2RL' to get rid of the tag structure as we don't
-- need it, and 'mapRL seal2' to get rid of the witnesses which we
-- also don't need. The result is of type '[Sealed2 p]', where 'p'
-- is the underlying patch type of the repository we are reading
-- (either darcs-1 or darcs-2)
patches <- mapRL seal2 . newset2RL <$> readRepo repo
-- Use the recently introduced "patch index" to filter the list of
-- patches from the repo down to ones that just touch 'fileName'.
--
-- This step is optional: we can remove it and the result will be
-- the same, but substantially slower on large repositories where
-- the patch we want is far back in the repo.
patches <- filterPatches repo [fileName] patches
-- Use 'filter' and 'listToMaybe' to get the first patch that touches
-- 'fileName'.
--
-- The filter is superfluous in this simple case if the patch
-- index was used, but doesn't cost much if so.
--
-- Note that this doesn't track renames, so isn't suitable for
-- finding anything but the last patch that touched 'fileName'.
--
-- 'unseal2' is used to lift a function that works on witnessed
-- patches to one that works on "sealed" patches.
let wanted = unseal2 (\patch -> fileName `elem` listTouchedFiles patch)
let thepatch = listToMaybe . filter wanted $ patches
-- Finally, return the metadata of the patch.
--
-- Things get a little bit more complex if we want to deal
-- with the contents of the patch, because the specific
-- patch type isn't known statically - it might be
-- darcs-1 or darcs-2.
--
-- The best approach is to write a polymorphic function that
-- can accept any instance of 'RepoPatch'.
return (fmap (unseal2 info) thepatch)