diff --git a/CHANGELOG b/CHANGELOG index fece6c4def..a9e21290ae 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ git-annex (10.20241203) UNRELEASED; urgency=medium + * Added config `url..annexInsteadOf` corresponding to git's + `url..pushInsteadOf`, to configure the urls to use for accessing + the git-annex repositories on a server without needing to configure + remote.name.annexUrl in each repository. * Work around git hash-object --stdin-paths's odd stripping of carriage return from the end of the line (some windows infection), avoiding crashing when the repo contains a filename ending in a carriage return. diff --git a/Git/Remote.hs b/Git/Remote.hs index 2cc0e2601d..b09aee6643 100644 --- a/Git/Remote.hs +++ b/Git/Remote.hs @@ -89,7 +89,7 @@ remoteLocationIsSshUrl _ = False parseRemoteLocation :: String -> Bool -> Repo -> RemoteLocation parseRemoteLocation s knownurl repo = go where - s' = calcloc s + s' = fromMaybe s $ insteadOfUrl s ".insteadof" $ fullconfig repo go #ifdef mingw32_HOST_OS | dosstyle s' = RemotePath (dospath s') @@ -98,28 +98,6 @@ parseRemoteLocation s knownurl repo = go | urlstyle s' = RemoteUrl s' | knownurl && s' == s = RemoteUrl s' | otherwise = RemotePath s' - -- insteadof config can rewrite remote location - calcloc l - | null insteadofs = l - | otherwise = replacement ++ drop (S.length bestvalue) l - where - replacement = decodeBS $ S.drop (S.length prefix) $ - S.take (S.length bestkey - S.length suffix) bestkey - (bestkey, bestvalue) = - case maximumBy longestvalue insteadofs of - (ConfigKey k, ConfigValue v) -> (k, v) - (ConfigKey k, NoConfigValue) -> (k, mempty) - longestvalue (_, a) (_, b) = compare b a - insteadofs = filterconfig $ \case - (ConfigKey k, ConfigValue v) -> - prefix `S.isPrefixOf` k && - suffix `S.isSuffixOf` k && - v `S.isPrefixOf` encodeBS l - (_, NoConfigValue) -> False - filterconfig f = filter f $ - concatMap splitconfigs $ M.toList $ fullconfig repo - splitconfigs (k, vs) = map (\v -> (k, v)) (NE.toList vs) - (prefix, suffix) = ("url." , ".insteadof") -- git supports URIs that contain unescaped characters such as -- spaces. So to test if it's a (git) URI, escape those. urlstyle v = isURI (escapeURIString isUnescapedInURI v) @@ -147,3 +125,26 @@ parseRemoteLocation s knownurl repo = go dosstyle = hasDrive dospath = fromRawFilePath . fromInternalGitPath . toRawFilePath #endif + +insteadOfUrl :: String -> S.ByteString -> RepoFullConfig -> Maybe String +insteadOfUrl u configsuffix fullcfg + | null insteadofs = Nothing + | otherwise = Just $ replacement ++ drop (S.length bestvalue) u + where + replacement = decodeBS $ S.drop (S.length configprefix) $ + S.take (S.length bestkey - S.length configsuffix) bestkey + (bestkey, bestvalue) = + case maximumBy longestvalue insteadofs of + (ConfigKey k, ConfigValue v) -> (k, v) + (ConfigKey k, NoConfigValue) -> (k, mempty) + longestvalue (_, a) (_, b) = compare b a + insteadofs = filterconfig $ \case + (ConfigKey k, ConfigValue v) -> + configprefix `S.isPrefixOf` k && + configsuffix `S.isSuffixOf` k && + v `S.isPrefixOf` encodeBS u + (_, NoConfigValue) -> False + filterconfig f = filter f $ + concatMap splitconfigs $ M.toList fullcfg + splitconfigs (k, vs) = map (\v -> (k, v)) (NE.toList vs) + configprefix = "url." diff --git a/Git/Types.hs b/Git/Types.hs index 18398a040e..b28380bc46 100644 --- a/Git/Types.hs +++ b/Git/Types.hs @@ -41,9 +41,9 @@ data RepoLocation data Repo = Repo { location :: RepoLocation - , config :: M.Map ConfigKey ConfigValue + , config :: RepoConfig -- a given git config key can actually have multiple values - , fullconfig :: M.Map ConfigKey (NE.NonEmpty ConfigValue) + , fullconfig :: RepoFullConfig -- 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 @@ -60,6 +60,10 @@ data Repo = Repo -- when using this repository. , repoPathSpecifiedExplicitly :: Bool } deriving (Show, Eq, Ord) + +type RepoConfig = M.Map ConfigKey ConfigValue + +type RepoFullConfig = M.Map ConfigKey (NE.NonEmpty ConfigValue) newtype ConfigKey = ConfigKey S.ByteString deriving (Ord, Eq) diff --git a/Remote/Git.hs b/Remote/Git.hs index 10d582bd36..d77fce1fd8 100644 --- a/Remote/Git.hs +++ b/Remote/Git.hs @@ -98,8 +98,9 @@ locationField = Accepted "location" list :: Bool -> Annex [Git.Repo] list autoinit = do - c <- fromRepo Git.config - rs <- mapM (tweakurl c) =<< Annex.getGitRemotes + cfg <- fromRepo Git.config + fullcfg <- fromRepo Git.fullconfig + rs <- mapM (tweakurl cfg fullcfg) =<< Annex.getGitRemotes rs' <- mapM (configRead autoinit) (filter (not . isGitRemoteAnnex) rs) proxies <- doQuietAction getProxies if proxies == mempty @@ -108,17 +109,20 @@ list autoinit = do proxied <- listProxied proxies rs' return (proxied++rs') where - tweakurl c r = do + tweakurl cfg fullcfg r = do let n = fromJust $ Git.remoteName r - case getAnnexUrl r c of - Just url | not (isP2PHttpProtocolUrl url) -> + case getAnnexUrl r cfg fullcfg of + Just url | not (isP2PHttpProtocolUrl url) -> inRepo $ \g -> Git.Construct.remoteNamed n $ Git.Construct.fromRemoteLocation url False g _ -> return r -getAnnexUrl :: Git.Repo -> M.Map Git.ConfigKey Git.ConfigValue -> Maybe String -getAnnexUrl r c = Git.fromConfigValue <$> M.lookup (annexUrlConfigKey r) c +getAnnexUrl :: Git.Repo -> Git.RepoConfig -> Git.RepoFullConfig -> Maybe String +getAnnexUrl r cfg fullcfg = + (Git.fromConfigValue <$> M.lookup (annexUrlConfigKey r) cfg) + <|> + annexInsteadOfUrl fullcfg (Git.repoLocation r) annexUrlConfigKey :: Git.Repo -> Git.ConfigKey annexUrlConfigKey r = remoteConfig r "annexurl" diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs index 4b9c546e86..2ab5de3ea6 100644 --- a/Types/GitConfig.hs +++ b/Types/GitConfig.hs @@ -27,6 +27,7 @@ module Types.GitConfig ( proxyInheritedFields, MkRemoteConfigKey, mkRemoteConfigKey, + annexInsteadOfUrl, ) where import Common @@ -35,7 +36,7 @@ import qualified Git.Config import qualified Git.Construct import Git.Types import Git.ConfigTypes -import Git.Remote (isRemoteKey, isLegalName, remoteKeyToRemoteName) +import Git.Remote (isRemoteKey, isLegalName, remoteKeyToRemoteName, insteadOfUrl) import Git.Branch (CommitMode(..)) import Git.Quote (QuotePath(..)) import Utility.DataUnits @@ -497,16 +498,14 @@ extractRemoteGitConfig r remotename = do , remoteAnnexClusterGateway = fromMaybe [] $ (mapMaybe (mkClusterUUID . toUUID) . words) <$> getmaybe ClusterGatewayField - , remoteUrl = case Git.Config.getMaybe (mkRemoteConfigKey remotename (remoteGitConfigKey UrlField)) r of - Just (ConfigValue b) - | B.null b -> Nothing - | otherwise -> Just (decodeBS b) - _ -> Nothing + , remoteUrl = getremoteurl , remoteAnnexP2PHttpUrl = case Git.Config.getMaybe (mkRemoteConfigKey remotename (remoteGitConfigKey AnnexUrlField)) r of Just (ConfigValue b) -> parseP2PHttpUrl (decodeBS b) - _ -> Nothing + _ -> parseP2PHttpUrl + =<< annexInsteadOfUrl (fullconfig r) + =<< getremoteurl , remoteAnnexShell = getmaybe ShellField , remoteAnnexSshOptions = getoptions SshOptionsField , remoteAnnexRsyncOptions = getoptions RsyncOptionsField @@ -544,6 +543,11 @@ extractRemoteGitConfig r remotename = do in Git.Config.getMaybe (mkRemoteConfigKey remotename k) r <|> Git.Config.getMaybe (mkAnnexConfigKey k) r getoptions k = fromMaybe [] $ words <$> getmaybe k + getremoteurl = case Git.Config.getMaybe (mkRemoteConfigKey remotename (remoteGitConfigKey UrlField)) r of + Just (ConfigValue b) + | B.null b -> Nothing + | otherwise -> Just (decodeBS b) + _ -> Nothing data RemoteGitConfigField = CostField @@ -742,3 +746,6 @@ remoteAnnexConfigEnd key = "annex-" <> key remoteConfig :: RemoteNameable r => r -> B.ByteString -> ConfigKey remoteConfig r key = ConfigKey $ "remote." <> encodeBS (getRemoteName r) <> "." <> key + +annexInsteadOfUrl :: RepoFullConfig -> String -> Maybe String +annexInsteadOfUrl fullcfg loc = insteadOfUrl loc ".annexinsteadof" fullcfg diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn index a082c97647..5e4c9f4777 100644 --- a/doc/git-annex.mdwn +++ b/doc/git-annex.mdwn @@ -1572,10 +1572,25 @@ Remotes are configured using these settings in `.git/config`. When this and the `remote..url` contain the same hostname, and this is an annex+http(s) url, and that is also a http(s) url, git-annex assumes that the same username and password can be used - for both urls. When password cacheing is configured, this allows + for both urls. When password caching is configured, this allows you to only be prompted once for a password when using both git and git-annex. See gitcredentials(7) for how to set up password caching. +* `url..annexInsteadOf` + + This works similarly to git's `url..pushInsteadOf`, rewriting + a remote url that starts with the value of this config to instead + start with ``. + + The rewritten url is used by git-annex for accessing the remote, + and works the same as `remote..annexUrl`, including supporting + annex+http urls. + + Note that git-annex also supports git's `url..insteadOf` + configuration. When both are set, the remote's url is first rewritten + by insteadOf, and that rewritten url can then be further + rewritten using annexInsteadOf. + * `remote..annex-uuid` git-annex caches UUIDs of remote repositories here. diff --git a/doc/todo/copy__47__move_support_for_pushinsteadOf_/comment_8_8604dfafa32ebb14281c2c866214a920._comment b/doc/todo/copy__47__move_support_for_pushinsteadOf_/comment_8_8604dfafa32ebb14281c2c866214a920._comment index 859b8b8f7f..ce84def6de 100644 --- a/doc/todo/copy__47__move_support_for_pushinsteadOf_/comment_8_8604dfafa32ebb14281c2c866214a920._comment +++ b/doc/todo/copy__47__move_support_for_pushinsteadOf_/comment_8_8604dfafa32ebb14281c2c866214a920._comment @@ -10,11 +10,14 @@ The same way `remote..annexUrl` corresponds to `remote..pushUrl`. You would need to set 2 configs, but the separation is clear. -And you could do it once in your global git config for whatever +And you could set it once in your global git config for whatever servers you commonly use. Another benefit to is that the new `git-annex p2phttp` server needs annexUrl to be configured to a different url than the git url when using it. annexInsteadOf would let that be configured a single time for all urls on a given git server. + +Update: Implemented that. Let me know if you think it solves your problem +well enough. """]]