2012-10-31 19:17:00 +00:00
|
|
|
{- git remote stuff
|
|
|
|
-
|
2015-01-21 16:50:09 +00:00
|
|
|
- Copyright 2012 Joey Hess <id@joeyh.name>
|
2012-10-31 19:17:00 +00:00
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2013-09-26 21:26:13 +00:00
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
|
2012-10-31 19:17:00 +00:00
|
|
|
module Git.Remote where
|
|
|
|
|
|
|
|
import Common
|
2013-09-18 19:30:53 +00:00
|
|
|
import Git
|
2013-11-07 22:02:00 +00:00
|
|
|
import Git.Types
|
2013-09-18 19:30:53 +00:00
|
|
|
|
2012-10-31 19:17:00 +00:00
|
|
|
import Data.Char
|
2013-09-26 21:26:13 +00:00
|
|
|
import qualified Data.Map as M
|
|
|
|
import Network.URI
|
2013-10-03 00:26:00 +00:00
|
|
|
#ifdef mingw32_HOST_OS
|
|
|
|
import Git.FilePath
|
|
|
|
#endif
|
2012-10-31 19:17:00 +00:00
|
|
|
|
2018-01-09 19:36:56 +00:00
|
|
|
{- Is a git config key one that specifies the location of a remote? -}
|
|
|
|
isRemoteKey :: String -> Bool
|
|
|
|
isRemoteKey k = "remote." `isPrefixOf` k && ".url" `isSuffixOf` k
|
|
|
|
|
|
|
|
{- Get a remote's name from the config key that specifies its location. -}
|
|
|
|
remoteKeyToRemoteName :: String -> RemoteName
|
2018-11-23 15:24:05 +00:00
|
|
|
remoteKeyToRemoteName k = intercalate "." $ dropFromEnd 1 $ drop 1 $ splitc '.' k
|
2018-01-09 19:36:56 +00:00
|
|
|
|
2012-10-31 19:17:00 +00:00
|
|
|
{- Construct a legal git remote name out of an arbitrary input string.
|
|
|
|
-
|
|
|
|
- There seems to be no formal definition of this in the git source,
|
|
|
|
- just some ad-hoc checks, and some other things that fail with certian
|
|
|
|
- types of names (like ones starting with '-').
|
|
|
|
-}
|
2013-09-18 19:30:53 +00:00
|
|
|
makeLegalName :: String -> RemoteName
|
2012-10-31 19:17:00 +00:00
|
|
|
makeLegalName s = case filter legal $ replace "/" "_" s of
|
|
|
|
-- it can't be empty
|
|
|
|
[] -> "unnamed"
|
|
|
|
-- it can't start with / or - or .
|
|
|
|
'.':s' -> makeLegalName s'
|
|
|
|
'/':s' -> makeLegalName s'
|
|
|
|
'-':s' -> makeLegalName s'
|
|
|
|
s' -> s'
|
|
|
|
where
|
|
|
|
{- Only alphanumerics, and a few common bits of punctuation common
|
|
|
|
- in hostnames. -}
|
|
|
|
legal '_' = True
|
|
|
|
legal '.' = True
|
|
|
|
legal c = isAlphaNum c
|
2013-09-18 19:30:53 +00:00
|
|
|
|
2013-09-26 21:26:13 +00:00
|
|
|
data RemoteLocation = RemoteUrl String | RemotePath FilePath
|
|
|
|
|
|
|
|
remoteLocationIsUrl :: RemoteLocation -> Bool
|
|
|
|
remoteLocationIsUrl (RemoteUrl _) = True
|
|
|
|
remoteLocationIsUrl _ = False
|
|
|
|
|
2013-11-07 22:02:00 +00:00
|
|
|
remoteLocationIsSshUrl :: RemoteLocation -> Bool
|
|
|
|
remoteLocationIsSshUrl (RemoteUrl u) = "ssh://" `isPrefixOf` u
|
|
|
|
remoteLocationIsSshUrl _ = False
|
|
|
|
|
2013-09-26 21:26:13 +00:00
|
|
|
{- Determines if a given remote location is an url, or a local
|
|
|
|
- path. Takes the repository's insteadOf configuration into account. -}
|
|
|
|
parseRemoteLocation :: String -> Repo -> RemoteLocation
|
|
|
|
parseRemoteLocation s repo = ret $ calcloc s
|
|
|
|
where
|
2014-10-09 18:53:13 +00:00
|
|
|
ret v
|
2013-09-26 21:26:13 +00:00
|
|
|
#ifdef mingw32_HOST_OS
|
|
|
|
| dosstyle v = RemotePath (dospath v)
|
|
|
|
#endif
|
|
|
|
| scpstyle v = RemoteUrl (scptourl v)
|
|
|
|
| urlstyle v = RemoteUrl v
|
|
|
|
| otherwise = RemotePath v
|
|
|
|
-- insteadof config can rewrite remote location
|
|
|
|
calcloc l
|
|
|
|
| null insteadofs = l
|
|
|
|
| otherwise = replacement ++ drop (length bestvalue) l
|
|
|
|
where
|
|
|
|
replacement = drop (length prefix) $
|
|
|
|
take (length bestkey - length suffix) bestkey
|
|
|
|
(bestkey, bestvalue) = maximumBy longestvalue insteadofs
|
|
|
|
longestvalue (_, a) (_, b) = compare b a
|
|
|
|
insteadofs = filterconfig $ \(k, v) ->
|
2017-05-16 03:32:17 +00:00
|
|
|
prefix `isPrefixOf` k &&
|
|
|
|
suffix `isSuffixOf` k &&
|
|
|
|
v `isPrefixOf` l
|
2013-09-26 21:26:13 +00:00
|
|
|
filterconfig f = filter f $
|
|
|
|
concatMap splitconfigs $ M.toList $ fullconfig repo
|
|
|
|
splitconfigs (k, vs) = map (\v -> (k, v)) vs
|
|
|
|
(prefix, suffix) = ("url." , ".insteadof")
|
|
|
|
urlstyle v = isURI v || ":" `isInfixOf` v && "//" `isInfixOf` v
|
|
|
|
-- git remotes can be written scp style -- [user@]host:dir
|
|
|
|
-- but foo::bar is a git-remote-helper location instead
|
|
|
|
scpstyle v = ":" `isInfixOf` v
|
|
|
|
&& not ("//" `isInfixOf` v)
|
|
|
|
&& not ("::" `isInfixOf` v)
|
|
|
|
scptourl v = "ssh://" ++ host ++ slash dir
|
|
|
|
where
|
2014-09-10 18:17:02 +00:00
|
|
|
(host, dir)
|
|
|
|
-- handle ipv6 address inside []
|
|
|
|
| "[" `isPrefixOf` v = case break (== ']') v of
|
|
|
|
(h, ']':':':d) -> (h ++ "]", d)
|
|
|
|
(h, ']':d) -> (h ++ "]", d)
|
|
|
|
(h, d) -> (h, d)
|
|
|
|
| otherwise = separate (== ':') v
|
2013-09-26 21:26:13 +00:00
|
|
|
slash d | d == "" = "/~/" ++ d
|
|
|
|
| "/" `isPrefixOf` d = d
|
|
|
|
| "~" `isPrefixOf` d = '/':d
|
|
|
|
| otherwise = "/~/" ++ d
|
|
|
|
#ifdef mingw32_HOST_OS
|
|
|
|
-- git on Windows will write a path to .git/config with "drive:",
|
|
|
|
-- which is not to be confused with a "host:"
|
|
|
|
dosstyle = hasDrive
|
|
|
|
dospath = fromInternalGitPath
|
|
|
|
#endif
|