How to use the darcs library to query information about patches?
Asked Answered
E

1

11

I want to write a Haskell program that queries information about a darcs repository. Instead of invoking the darcs executable and parse the results, I would rather use the darcs library directly. It is said to be "very much work in progress" and to "lack a stable API", but seems usable.

I think I could answer my questions by studying the darcsden source code, for example, starting with this module, but I think it may be helpful not only to me if someone knowledgeable would provide a commented introduction to supplement such a study.

So, here is a concrete example.

How can I compute for a given file the most recent patch that affected it along with the date, author, and name of the patch? It would be extra useful if you explain the key library functions used in your solution.


Edit:

Here are some remarks that might not be obvious to someone unfamiliar with the source code of darcs. I learned them from Jason Dagit's master thesis and hope they are helpful to understand the answer given by Ganesh.

In Darcs, patches have a pre- and a post-context representing the state of the repository before and after applying the patch. In source code, these contexts are modeled using phantom types on the type of patches. These phantom types are called witnesses and seal2 is used to get rid of them.

In lists of patches, only the very first pre-context and the very last post-context are represented in the type. All other contexts are hidden using existential types. Darcs defines forward lists (called FL) and reverse lists (called RL). Reverse lists store patches in reverse (chronological) order (modulo patch reordering done by darcs). Reverse lists can be used to access the most recent patch in the head position. All functions with RL in their name create or operate on such reverse lists.

Ergo answered 17/10, 2012 at 19:21 Comment(3)
Do you want to use the last release (2.8.2) or the latest unstable version? There's some significant reorganisation in progress for 2.10, at least of the module structure.Top
The version I want to use depends on when 2.10 is going to be released. I'd prefer 2.10 if it will be released in the next one or two months. If only the module structure is different, I think it should be easy to transfer a solution in either version to the other. So feel free to give a future proof answer for 2.10 and hint at what would be different in 2.8.Ergo
2.10 is unlikely to be out in the next one or two months, but hopefully around the end of the year.Top
T
4
-- 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)
Top answered 18/10, 2012 at 12:9 Comment(5)
Thanks! In 2.8.2, withRepositoryDirectory expexts a list of DarcsFlags where you pass YesUseCache. The onle cache-related flag seems to be NoCache which suggests that passing the empty list has the same behaviour as YesUseCache. Correct?Ergo
I edited my question to include some (hopefully accurate) remarks on "witnesses" and reverse lists. I hope they make your answer more accessible for people unfamiliar with the Darcs source code.Ergo
Thanks! I did think I should provide some commentary on them myself but didn't get round to it.Top
Does it exist a C/C++ API for Darcs?Disraeli
No, it's Haskell only.Top

© 2022 - 2024 — McMap. All rights reserved.