Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the darcs library to query information about patches?

Tags:

haskell

darcs

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.

like image 476
Sebastian Fischer Avatar asked Oct 17 '12 19:10

Sebastian Fischer


1 Answers

-- 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)
like image 158
GS - Apologise to Monica Avatar answered Sep 26 '22 19:09

GS - Apologise to Monica