43f31121a5
This is a nice win. Avoids partial functions, by encoding at the type level the fact that fullconfig is never an empty list.
203 lines
5.7 KiB
Haskell
203 lines
5.7 KiB
Haskell
{- git data types
|
|
-
|
|
- Copyright 2010-2020 Joey Hess <id@joeyh.name>
|
|
-
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
-}
|
|
|
|
{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-}
|
|
|
|
module Git.Types where
|
|
|
|
import Network.URI
|
|
import Data.String
|
|
import Data.Default
|
|
import qualified Data.Map as M
|
|
import qualified Data.ByteString as S
|
|
import qualified Data.List.NonEmpty as NE
|
|
import System.Posix.Types
|
|
import Utility.SafeCommand
|
|
import Utility.FileSystemEncoding
|
|
import qualified Data.Semigroup as Sem
|
|
import Prelude
|
|
|
|
{- Support repositories on local disk, and repositories accessed via an URL.
|
|
-
|
|
- Repos on local disk have a git directory, and unless bare, a worktree.
|
|
-
|
|
- A local repo may not have had its config read yet, in which case all
|
|
- that's known about it is its path.
|
|
-
|
|
- Finally, an Unknown repository may be known to exist, but nothing
|
|
- else known about it.
|
|
-}
|
|
data RepoLocation
|
|
= Local { gitdir :: RawFilePath, worktree :: Maybe RawFilePath }
|
|
| LocalUnknown RawFilePath
|
|
| Url URI
|
|
| UnparseableUrl String
|
|
| Unknown
|
|
deriving (Show, Eq, Ord)
|
|
|
|
data Repo = Repo
|
|
{ location :: RepoLocation
|
|
, config :: M.Map ConfigKey ConfigValue
|
|
-- a given git config key can actually have multiple values
|
|
, fullconfig :: M.Map ConfigKey (NE.NonEmpty ConfigValue)
|
|
-- remoteName holds the name used for this repo in some other
|
|
-- repo's list of remotes, when this repo is such a remote
|
|
, remoteName :: Maybe RemoteName
|
|
-- alternate environment to use when running git commands
|
|
, gitEnv :: Maybe [(String, String)]
|
|
, gitEnvOverridesGitDir :: Bool
|
|
-- global options to pass to git when running git commands
|
|
, gitGlobalOpts :: [CommandParam]
|
|
-- True only when --git-dir or GIT_DIR was used
|
|
, gitDirSpecifiedExplicitly :: Bool
|
|
-- Use when the path to the repository was specified explicitly,
|
|
-- eg in a git remote, and so it's safe to set
|
|
-- -c safe.directory=* and -c safe.bareRepository=all
|
|
-- when using this repository.
|
|
, repoPathSpecifiedExplicitly :: Bool
|
|
} deriving (Show, Eq, Ord)
|
|
|
|
newtype ConfigKey = ConfigKey S.ByteString
|
|
deriving (Ord, Eq)
|
|
|
|
data ConfigValue
|
|
= ConfigValue S.ByteString
|
|
| NoConfigValue
|
|
-- ^ git treats a setting with no value as different than a setting
|
|
-- with an empty value
|
|
deriving (Ord, Eq)
|
|
|
|
instance Sem.Semigroup ConfigValue where
|
|
ConfigValue a <> ConfigValue b = ConfigValue (a <> b)
|
|
a <> NoConfigValue = a
|
|
NoConfigValue <> b = b
|
|
|
|
instance Monoid ConfigValue where
|
|
mempty = ConfigValue mempty
|
|
|
|
instance Default ConfigValue where
|
|
def = ConfigValue mempty
|
|
|
|
fromConfigKey :: ConfigKey -> String
|
|
fromConfigKey (ConfigKey s) = decodeBS s
|
|
|
|
fromConfigKey' :: ConfigKey -> S.ByteString
|
|
fromConfigKey' (ConfigKey s) = s
|
|
|
|
instance Show ConfigKey where
|
|
show = fromConfigKey
|
|
|
|
class FromConfigValue a where
|
|
fromConfigValue :: ConfigValue -> a
|
|
|
|
instance FromConfigValue S.ByteString where
|
|
fromConfigValue (ConfigValue s) = s
|
|
fromConfigValue NoConfigValue = mempty
|
|
|
|
instance FromConfigValue String where
|
|
fromConfigValue = decodeBS . fromConfigValue
|
|
|
|
instance Show ConfigValue where
|
|
show = fromConfigValue
|
|
|
|
instance IsString ConfigKey where
|
|
fromString = ConfigKey . encodeBS
|
|
|
|
instance IsString ConfigValue where
|
|
fromString = ConfigValue . encodeBS
|
|
|
|
type RemoteName = String
|
|
|
|
{- A git ref. Can be a sha1, or a branch or tag name. -}
|
|
newtype Ref = Ref S.ByteString
|
|
deriving (Eq, Ord, Read, Show)
|
|
|
|
fromRef :: Ref -> String
|
|
fromRef = decodeBS . fromRef'
|
|
|
|
fromRef' :: Ref -> S.ByteString
|
|
fromRef' (Ref s) = s
|
|
|
|
{- Aliases for Ref. -}
|
|
type Branch = Ref
|
|
type Sha = Ref
|
|
type Tag = Ref
|
|
|
|
{- A date in the format described in gitrevisions. Includes the
|
|
- braces, eg, "{yesterday}" -}
|
|
newtype RefDate = RefDate String
|
|
|
|
{- Types of objects that can be stored in git. -}
|
|
data ObjectType = BlobObject | CommitObject | TreeObject
|
|
deriving (Show)
|
|
|
|
readObjectType :: S.ByteString -> Maybe ObjectType
|
|
readObjectType "blob" = Just BlobObject
|
|
readObjectType "commit" = Just CommitObject
|
|
readObjectType "tree" = Just TreeObject
|
|
readObjectType _ = Nothing
|
|
|
|
fmtObjectType :: ObjectType -> S.ByteString
|
|
fmtObjectType BlobObject = "blob"
|
|
fmtObjectType CommitObject = "commit"
|
|
fmtObjectType TreeObject = "tree"
|
|
|
|
{- Types of items in a tree. -}
|
|
data TreeItemType
|
|
= TreeFile
|
|
| TreeExecutable
|
|
| TreeSymlink
|
|
| TreeSubmodule
|
|
| TreeSubtree
|
|
deriving (Eq, Show)
|
|
|
|
{- Git uses magic numbers to denote the type of a tree item. -}
|
|
readTreeItemType :: S.ByteString -> Maybe TreeItemType
|
|
readTreeItemType "100644" = Just TreeFile
|
|
readTreeItemType "100755" = Just TreeExecutable
|
|
readTreeItemType "120000" = Just TreeSymlink
|
|
readTreeItemType "160000" = Just TreeSubmodule
|
|
readTreeItemType "040000" = Just TreeSubtree
|
|
readTreeItemType _ = Nothing
|
|
|
|
fmtTreeItemType :: TreeItemType -> S.ByteString
|
|
fmtTreeItemType TreeFile = "100644"
|
|
fmtTreeItemType TreeExecutable = "100755"
|
|
fmtTreeItemType TreeSymlink = "120000"
|
|
fmtTreeItemType TreeSubmodule = "160000"
|
|
fmtTreeItemType TreeSubtree = "040000"
|
|
|
|
toTreeItemType :: FileMode -> Maybe TreeItemType
|
|
toTreeItemType 0o100644 = Just TreeFile
|
|
toTreeItemType 0o100755 = Just TreeExecutable
|
|
toTreeItemType 0o120000 = Just TreeSymlink
|
|
toTreeItemType 0o160000 = Just TreeSubmodule
|
|
toTreeItemType 0o040000 = Just TreeSubtree
|
|
toTreeItemType _ = Nothing
|
|
|
|
fromTreeItemType :: TreeItemType -> FileMode
|
|
fromTreeItemType TreeFile = 0o100644
|
|
fromTreeItemType TreeExecutable = 0o100755
|
|
fromTreeItemType TreeSymlink = 0o120000
|
|
fromTreeItemType TreeSubmodule = 0o160000
|
|
fromTreeItemType TreeSubtree = 0o040000
|
|
|
|
data Commit = Commit
|
|
{ commitTree :: Sha
|
|
, commitParent :: [Sha]
|
|
, commitAuthorMetaData :: CommitMetaData
|
|
, commitCommitterMetaData :: CommitMetaData
|
|
, commitMessage :: String
|
|
}
|
|
deriving (Show)
|
|
|
|
data CommitMetaData = CommitMetaData
|
|
{ commitName :: Maybe String
|
|
, commitEmail :: Maybe String
|
|
, commitDate :: Maybe String -- In raw git form, "epoch -tzoffset"
|
|
}
|
|
deriving (Show)
|