diff options
author | Joey Hess <joey@kitenet.net> | 2011-03-03 21:02:29 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2011-03-03 21:02:29 -0400 |
commit | 42259eee9200588f69af2b56557d5d191d426ad0 (patch) | |
tree | 7595b3571c787d926b1d66a49e8bdd11cd23e772 | |
parent | b27b0d5cd463afcce8a95730a49288edd89eb8b8 (diff) |
support git funky remote syntaxes
* Look for dir.git directories the same as git does.
* Support remote urls specified as relative paths.
* Support non-ssh remote paths that contain tilde expansions.
-rw-r--r-- | GitRepo.hs | 114 | ||||
-rw-r--r-- | debian/changelog | 3 | ||||
-rw-r--r-- | git-annex-shell.hs | 4 |
3 files changed, 77 insertions, 44 deletions
diff --git a/GitRepo.hs b/GitRepo.hs index 8e3f14ee5..bb0e3d5b7 100644 --- a/GitRepo.hs +++ b/GitRepo.hs @@ -11,7 +11,7 @@ module GitRepo ( Repo, repoFromCwd, - repoFromPath, + repoFromAbsPath, repoFromUrl, localToUrl, repoIsUrl, @@ -49,7 +49,7 @@ module GitRepo ( encodeGitFile, typeChangedFiles, typeChangedStagedFiles, - absDir, + repoAbsPath, reap, prop_idempotent_deencode @@ -57,6 +57,7 @@ module GitRepo ( import Control.Monad (unless) import System.Directory +import System.FilePath import System.Posix.Directory import System.Posix.User import System.Posix.Process @@ -98,15 +99,23 @@ newFrom l = remoteName = Nothing } -{- Local Repo constructor. -} -repoFromPath :: FilePath -> Repo -repoFromPath dir = newFrom $ Dir dir +{- Local Repo constructor, requires an absolute path to the repo be + - specified. -} +repoFromAbsPath :: FilePath -> IO Repo +repoFromAbsPath dir + | "/" `isPrefixOf` dir = do + -- Git always looks for "dir.git" in preference to + -- to "dir", even if dir ends in a "/". + let dir' = (dropTrailingPathSeparator dir) ++ ".git" + e <- doesDirectoryExist dir' + return $ newFrom $ Dir $ if e then dir' else dir + | otherwise = error $ "internal error, " ++ dir ++ " is not absolute" {- Remote Repo constructor. Throws exception on invalid url. -} -repoFromUrl :: String -> Repo +repoFromUrl :: String -> IO Repo repoFromUrl url - | startswith "file://" url = repoFromPath $ uriPath u - | otherwise = newFrom $ Url u + | startswith "file://" url = repoFromAbsPath $ uriPath u + | otherwise = return $ newFrom $ Url u where u = case (parseURI url) of Just v -> v @@ -356,31 +365,35 @@ configRead r = assertLocal r $ error "internal" hConfigRead :: Repo -> Handle -> IO Repo hConfigRead repo h = do val <- hGetContentsStrict h - return $ configStore repo val + configStore repo val {- Parses a git config and returns a version of the repo using it. -} -configStore :: Repo -> String -> Repo -configStore repo s = r { remotes = configRemotes r } - where r = repo { config = configParse s } +configStore :: Repo -> String -> IO Repo +configStore repo s = do + rs <- configRemotes r + return $ r { remotes = rs } + where + r = repo { config = configParse s } {- Checks if a string from git config is a true value. -} configTrue :: String -> Bool configTrue s = map toLower s == "true" {- Calculates a list of a repo's configured remotes, by parsing its config. -} -configRemotes :: Repo -> [Repo] -configRemotes repo = map construct remotepairs +configRemotes :: Repo -> IO [Repo] +configRemotes repo = mapM construct remotepairs where remotepairs = Map.toList $ filterremotes $ config repo filterremotes = Map.filterWithKey (\k _ -> isremote k) isremote k = startswith "remote." k && endswith ".url" k remotename k = split "." k !! 1 - construct (k,v) = (gen v) { remoteName = Just $ remotename k } + construct (k,v) = do + r <- gen v + return $ r { remoteName = Just $ remotename k } gen v | scpstyle v = repoFromUrl $ scptourl v | isURI v = repoFromUrl v - | otherwise = repoFromPath v + | otherwise = repoFromRemotePath v repo -- git remotes can be written scp style -- [user@]host:dir - -- where dir is relative to the user's home directory. scpstyle v = ":" `isInfixOf` v && (not $ "//" `isInfixOf` v) scptourl v = "ssh://" ++ host ++ slash dir where @@ -389,6 +402,7 @@ configRemotes repo = map construct remotepairs dir = join ":" $ drop 1 bits slash d | d == "" = "/~/" ++ dir | d !! 0 == '/' = dir + | d !! 0 == '~' = '/':dir | otherwise = "/~/" ++ dir {- Parses git config --list output into a config map. -} @@ -503,37 +517,51 @@ encodeGitFile s = foldl (++) "\"" (map echar s) ++ "\"" e_utf c = concat $ map showoctal $ (encode [c] :: [Word8]) - {- for quickcheck -} prop_idempotent_deencode :: String -> Bool prop_idempotent_deencode s = s == decodeGitFile (encodeGitFile s) -{- Git ssh remotes can have a directory that is specified relative - - to a home directory. This converts such a directory to an absolute path. - - Note that it has to run on the remote system. +{- Constructs a Repo from the path specified in the git remotes of + - another Repo. -} +repoFromRemotePath :: FilePath -> Repo -> IO Repo +repoFromRemotePath dir repo = do + dir' <- expandTilde dir + repoFromAbsPath $ workTree repo </> dir' + +{- Git remotes can have a directory that is specified relative + - to the user's home directory, or that contains tilde expansions. + - This converts such a directory to an absolute path. + - Note that it has to run on the system where the remote is. -} -absDir :: String -> IO String -absDir d - | "/" `isPrefixOf` d = expandt d - | otherwise = do - h <- myhomedir - return $ h ++ d +repoAbsPath :: FilePath -> IO FilePath +repoAbsPath d = do + d' <- expandTilde d + h <- myHomeDir + hPutStrLn stderr $ "repoAbsPath " ++ d + return $ h </> d' + +myHomeDir :: IO FilePath +myHomeDir = do + uid <- getEffectiveUserID + u <- getUserEntryForID uid + return $ homeDirectory u + +expandTilde :: FilePath -> IO FilePath +expandTilde = expandt True where - homedir u = (homeDirectory u) ++ "/" - myhomedir = do - uid <- getEffectiveUserID - u <- getUserEntryForID uid - return $ homedir u - expandt [] = return "" - expandt ('/':'~':'/':cs) = do - h <- myhomedir - return $ h ++ cs - expandt ('/':'~':cs) = do + expandt _ [] = return "" + expandt _ ('/':cs) = do + v <- expandt True cs + return ('/':v) + expandt True ('~':'/':cs) = do + h <- myHomeDir + return $ h </> cs + expandt True ('~':cs) = do let (name, rest) = findname "" cs u <- getUserEntryForName name - return $ homedir u ++ rest - expandt (c:cs) = do - v <- expandt cs + return $ homeDirectory u </> rest + expandt _ (c:cs) = do + v <- expandt False cs return (c:v) findname n [] = (n, "") findname n (c:cs) @@ -546,10 +574,12 @@ repoFromCwd = do cwd <- getCurrentDirectory top <- seekUp cwd isRepoTop case top of - (Just dir) -> return $ repoFromPath dir + -- repoFromAbsPath is not used to avoid looking for + -- "dir.git" directories. + (Just dir) -> return $ newFrom $ Dir dir Nothing -> error "Not in a git repository." -seekUp :: String -> (String -> IO Bool) -> IO (Maybe String) +seekUp :: FilePath -> (FilePath -> IO Bool) -> IO (Maybe FilePath) seekUp dir want = do ok <- want dir if ok diff --git a/debian/changelog b/debian/changelog index ca1c51c4b..a854235ce 100644 --- a/debian/changelog +++ b/debian/changelog @@ -16,6 +16,9 @@ git-annex (0.22) UNRELEASED; urgency=low use caution! * describe: New subcommand that can set or change the description of a repository. + * Look for dir.git directories the same as git does. + * Support remote urls specified as relative paths. + * Support non-ssh remote paths that contain tilde expansions. -- Joey Hess <joeyh@debian.org> Sun, 13 Feb 2011 00:48:02 -0400 diff --git a/git-annex-shell.hs b/git-annex-shell.hs index aeaadcbf8..e8a744748 100644 --- a/git-annex-shell.hs +++ b/git-annex-shell.hs @@ -60,8 +60,8 @@ builtins = map cmdname cmds builtin :: String -> String -> [String] -> IO () builtin cmd dir params = do - dir' <- Git.absDir dir - let gitrepo = Git.repoFromPath dir' + dir' <- Git.repoAbsPath dir + gitrepo <- Git.repoFromAbsPath dir' dispatch gitrepo (cmd:(filterparams params)) cmds commonOptions header external :: [String] -> IO () |