moduleStorage.Hashed(-- * Obtaining Trees.---- | Please note that Trees obtained this way will contain Stub-- items. These need to be executed (they are IO actions) in order to be-- accessed. Use 'expand' to do this. However, many operations are-- perfectly fine to be used on a stubbed Tree (and it is often more-- efficient to do everything that can be done before expanding a Tree).readPlainTree,readDarcsHashed,readDarcsPristine-- * Blob access.,read,readSegment-- * Writing trees.,writePlainTree-- * Unsafe functions for the curious explorer.---- | These are more useful for playing within ghci than for real, serious-- programs. They generally trade safety for conciseness. Please use-- responsibly. Don't kill innocent kittens.,floatPath,printPath)whereimportPreludehiding(catch,read,lines)importqualifiedData.ByteString.Char8asBSimportqualifiedData.ByteString.Lazy.Char8asBLimportStorage.Hashed.AnchoredPathimportStorage.Hashed.UtilsimportStorage.Hashed.DarcsimportStorage.Hashed.Tree(Tree(listImmediate),TreeItem(..),ItemType(..),Blob(..),emptyTree,makeTree,makeTreeWithHash,list,read,find)importSystem.FilePath((</>),splitDirectories,normalise,dropTrailingPathSeparator)importSystem.Directory(getDirectoryContents,doesFileExist,doesDirectoryExist,createDirectoryIfMissing)importCodec.Compression.GZip(decompress)importControl.Monad(forM_,unless)importBundled.Posix(getFileStatus,isDirectory,FileStatus)-------------------------- For explorers---- | Take a relative FilePath and turn it into an AnchoredPath. The operation-- is unsafe and if you break it, you keep both pieces. More useful for-- exploratory purposes (ghci) than for serious programming.floatPath::FilePath->AnchoredPathfloatPath=AnchoredPath.map(Name.BS.pack).splitDirectories.normalise.dropTrailingPathSeparator-- | Take a relative FilePath within a Tree and print the contents of the-- object there. Useful for exploration, less so for serious programming.printPath::Tree->FilePath->IO()printPathtp=print'$findt(floatPathp)whereprint'Nothing=putStrLn$"ERROR: No object at "++pprint'(Just(Fileb))=doputStrLn$"== Contents of file "++p++":"BL.unpack`fmap`readb>>=putStrprint'(Just(SubTreet'))=doputStrLn$"== Listing Tree "++p++" (immediates only):"putStr$unlines$mapBS.unpack$listNamest'print'(Just(Stub__))=putStrLn$"== (not listing stub at "++p++")"listNamest'=[n|(Namen,_)<-listImmediatet']readPlainDir::FilePath->IO[(FilePath,FileStatus)]readPlainDirdir=withCurrentDirectorydir$doitems<-getDirectoryContents"."sequence[dost<-getFileStatussreturn(s,st)|s<-items,not$s`elem`[".",".."]]-- | Read in a plain directory hierarchy from a filesystem. NB. The 'read'-- function on Blobs with such a Tree is susceptible to file content-- changes. Since we use mmap in 'read', this will break referential-- transparency and produce unexpected results. Please always make sure that-- all parallel access to the underlying filesystem tree never mutates-- files. Unlink + recreate is fine though (in other words, the sync/write-- operations below are safe).readPlainTree::FilePath->IOTreereadPlainTreedir=doitems<-readPlainDirdirletsubs=[letname=nameFromFilePathname'inifisDirectorystatusthen(name,Stub(readPlainTree(dir</>name'))Nothing)else(name,File$Blob(readBlobname)Nothing)|(name',status)<-items]return$makeTreesubswherereadBlob(Namename)=readSegment(dir</>BS.unpackname,Nothing)-- | Read and parse a darcs-style hashed directory listing from a given @dir@-- and with a given @hash@.readDarcsHashedDir::FilePath->Hash->IO[(ItemType,Name,Hash)]readDarcsHashedDirdirh=docompressed<-BL.readFile(dir</>BS.unpack(darcsFormatHashh))letcontent=decompresscompressedlines=BL.split'\n'contentreturn$ifBL.nullcompressedthen[]elseparselineswhereparse(t:n:h':r)=(parse't,Name$BS.pack$darcsDecodeWhite(BL.unpackn),makeHashhash):parserwherehash=BS.concat$BL.toChunksh'parse_=[]parse'x|x==BL.pack"file:"=BlobType|x==BL.pack"directory:"=TreeType|otherwise=error$"Error parsing darcs hashed dir: "++BL.unpackx-- | Read in a darcs-style hashed tree. This is mainly useful for reading-- \"pristine.hashed\". You need to provide the root hash you are interested in-- (found in _darcs/hashed_inventory).readDarcsHashed::FilePath->Hash->IOTreereadDarcsHasheddirroot=doitems<-readDarcsHashedDirdirrootsubs<-sequence[casetpofBlobType->return(d,File$Blob(readBlobh)(Justh))TreeType->dolett=readDarcsHasheddirhreturn(d,Stubt(Justh))|(tp,d,h)<-items]return$makeTreeWithHashsubsrootwherelocationh=(dir</>BS.unpack(darcsFormatHashh),Nothing)readBlob=fmapdecompress.readSegment.location-- | Read in a darcs pristine tree. Handles the plain and hashed pristine-- cases. Does not (and will not) handle the no-pristine case, since that-- requires replaying patches. Cf. 'readDarcsHashed' and 'readPlainTree' that-- are used to do the actual 'Tree' construction.readDarcsPristine::FilePath->IOTreereadDarcsPristinedir=doletdarcs=dir</>"_darcs"h_inventory=darcs</>"hashed_inventory"repo<-doesDirectoryExistdarcsunlessrepo$fail$"Not a darcs repository: "++dirhashed<-doesFileExisth_inventoryifhashedthendoinv<-BS.readFileh_inventoryletlines=BS.split'\n'invcaselinesof[]->returnemptyTree(pris_line:_)->lethash=makeHash$BS.drop9pris_lineinreadDarcsHashed(darcs</>"pristine.hashed")hashelsereadPlainTree$darcs</>"pristine"-- | Write out *full* tree to a plain directory structure. If you instead want-- to make incremental updates, refer to "Monad.plainTreeIO".writePlainTree::Tree->FilePath->IO()writePlainTreetdir=docreateDirectoryIfMissingTruedirforM_(listt)writewherewrite(p,Fileb)=write'pbwrite(p,SubTree_)=createDirectoryIfMissingTrue(anchorPathdirp)write_=return()write'pb=readb>>=BL.writeFile(anchorPathdirp)