Xget Git 协议支持详解
概述
本文档详细介绍了 Xget 对 Git 协议的全面支持,包括 HTTP/HTTPS、SSH、Git 协议等各种 Git 传输协议的加速方案。通过 Xget,开发者可以显著提升 Git 操作的速度和稳定性。
Git 协议基础
Git 传输协议
Git 支持多种传输协议:
HTTP/HTTPS 协议
SSH 协议
Git 协议
Git 协议 (SSH)
Git 操作类型
Git 操作主要分为以下类型:
Clone
- 克隆完整仓库
- 浅克隆(shallow clone)
- 单分支克隆
Fetch
Pull
Push
其他操作
- LFS 操作
- Submodule 操作
- Archive 操作
HTTP/HTTPS 协议支持
基本原理
Xget 通过 HTTP/HTTPS 协议代理 Git 操作,实现以下功能:
URL 转换
- 将原始 Git URL 转换为 Xget 加速 URL
- 自动识别 Git 仓库地址
请求代理
- 代理 Git HTTP 请求
- 处理 Git 特定的请求头
响应缓存
URL 转换规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| function convertGitHubUrl(url) { const pattern = /^https?:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/; const match = url.match(pattern); if (match) { const [, user, repo] = match; return `https://xget.xi-xu.me/gh/${user}/${repo}.git`; } return url; }
function convertGitLabUrl(url) { const pattern = /^https?:\/\/gitlab\.com\/(.+?)\/(.+?)(\.git)?$/; const match = url.match(pattern); if (match) { const [, user, repo] = match; return `https://xget.xi-xu.me/gl/${user}/${repo}.git`; } return url; }
function convertGitUrl(url) { const converters = [ convertGitHubUrl, convertGitLabUrl, ]; for (const converter of converters) { const converted = converter(url); if (converted !== url) { return converted; } } return url; }
|
Git HTTP 请求处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
| async function handleGitHttpRequest(request) { const url = new URL(request.url); const path = url.pathname; if (path.endsWith('/info/refs')) { return await handleInfoRefs(request); } if (path.includes('/objects/')) { return await handleGitObject(request); } if (path.includes('/git-upload-pack')) { return await handleUploadPack(request); } if (path.includes('/git-receive-pack')) { return await handleReceivePack(request); } return await fetch(request); }
async function handleInfoRefs(request) { const url = new URL(request.url); const service = url.searchParams.get('service'); const targetUrl = buildTargetUrl(url); const headers = new Headers(request.headers); headers.set('Git-Protocol', 'version=2'); const response = await fetch(targetUrl, { method: request.method, headers: headers }); return response; }
async function handleGitObject(request) { const url = new URL(request.url); const targetUrl = buildTargetUrl(url); const cacheKey = `git-object:${url.pathname}`; const cached = await CACHE.get(cacheKey); if (cached) { return new Response(cached, { headers: { 'X-Cache': 'HIT', 'Content-Type': 'application/x-git-loose-object' } }); } const response = await fetch(targetUrl, { method: request.method, headers: request.headers }); if (response.ok) { const clonedResponse = response.clone(); const buffer = await clonedResponse.arrayBuffer(); await CACHE.put(cacheKey, buffer, { expirationTtl: 86400 }); } return response; }
async function handleUploadPack(request) { const url = new URL(request.url); const targetUrl = buildTargetUrl(url); const body = await request.arrayBuffer(); const response = await fetch(targetUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-git-upload-pack-request', 'Accept': 'application/x-git-upload-pack-result' }, body: body }); return response; }
async function handleReceivePack(request) { const url = new URL(request.url); const targetUrl = buildTargetUrl(url); const body = await request.arrayBuffer(); const response = await fetch(targetUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-git-receive-pack-request', 'Accept': 'application/x-git-receive-pack-result' }, body: body }); await invalidateGitCache(url.pathname); return response; }
function buildTargetUrl(url) { const path = url.pathname.replace(/^\/(gh|gl|gitea)\//, ''); const platform = url.pathname.split('/')[1]; const baseUrl = getPlatformBaseUrl(platform); return `${baseUrl}/${path}`; }
function getPlatformBaseUrl(platform) { const platforms = { 'gh': 'https://github.com', 'gl': 'https://gitlab.com', 'gitea': 'https://gitea.com' }; return platforms[platform] || 'https://github.com'; }
|
Git LFS 支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| async function handleGitLfsRequest(request) { const url = new URL(request.url); const path = url.pathname; if (path.includes('/objects/')) { return await handleLFSObject(request); } if (path.includes('/objects/batch')) { return await handleLFSBatch(request); } return await fetch(request); }
async function handleLFSObject(request) { const url = new URL(request.url); const targetUrl = buildTargetUrl(url); const cacheKey = `lfs-object:${url.pathname}`; const cached = await CACHE.get(cacheKey); if (cached) { return new Response(cached, { headers: { 'X-Cache': 'HIT', 'Content-Type': 'application/vnd.git-lfs' } }); } const response = await fetch(targetUrl, { method: request.method, headers: request.headers }); if (response.ok) { const clonedResponse = response.clone(); const buffer = await clonedResponse.arrayBuffer(); await CACHE.put(cacheKey, buffer, { expirationTtl: 604800 }); } return response; }
async function handleLFSBatch(request) { const url = new URL(request.url); const targetUrl = buildTargetUrl(url); const body = await request.json(); const response = await fetch(targetUrl, { method: 'POST', headers: { 'Content-Type': 'application/vnd.git-lfs+json', 'Accept': 'application/vnd.git-lfs+json' }, body: JSON.stringify(body) }); return response; }
|
SSH 协议支持
SSH 代理原理
Xget 通过 SSH 代理实现 Git SSH 协议的加速:
SSH 连接代理
数据传输优化
连接复用
SSH URL 转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function convertSSHUrl(url) { const sshPattern = /^git@([^:]+):(.+)$/; const match = url.match(sshPattern); if (match) { const [, host, repoPath] = match; const platform = getPlatformFromHost(host); return `ssh://[email protected]:22/${platform}/${repoPath}`; } return url; }
function getPlatformFromHost(host) { const platforms = { 'github.com': 'gh', 'gitlab.com': 'gl', 'gitea.com': 'gitea' }; return platforms[host] || 'gh'; }
|
SSH 配置
在 ~/.ssh/config 中配置 Xget SSH 代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # GitHub Host github.com HostName xget.xi-xu.me Port 22 User git IdentityFile ~/.ssh/id_rsa ProxyCommand ssh -W %h:%p [email protected]
# GitLab Host gitlab.com HostName xget.xi-xu.me Port 22 User git IdentityFile ~/.ssh/id_rsa ProxyCommand ssh -W %h:%p [email protected]
|
SSH 密钥管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| async function verifySSHKey(publicKey) { const keyPattern = /^(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521) /; if (!keyPattern.test(publicKey)) { throw new Error('Invalid SSH key format'); } const keyParts = publicKey.split(' '); if (keyParts.length < 2) { throw new Error('Invalid SSH key format'); } return true; }
async function storeSSHKey(userId, publicKey) { const key = `ssh-key:${userId}`; await KV.put(key, publicKey, { expirationTtl: 2592000 }); }
async function getSSHKey(userId) { const key = `ssh-key:${userId}`; return await KV.get(key); }
|
Git 协议支持
Git 协议原理
Git 协议是 Git 专用的传输协议,特点:
高效传输
无加密
简单实现
Git 协议 URL 转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function convertGitProtocolUrl(url) { const gitPattern = /^git:\/\/([^\/]+)\/(.+)$/; const match = url.match(gitPattern); if (match) { const [, host, repoPath] = match; const platform = getPlatformFromHost(host); return `git://xget.xi-xu.me/${platform}/${repoPath}`; } return url; }
|
Git 协议处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| async function handleGitProtocolRequest(request) { const url = new URL(request.url); const path = url.pathname; if (url.protocol === 'git:') { return await handleGitProtocol(request); } return await fetch(request); }
async function handleGitProtocol(request) { const url = new URL(request.url); const targetUrl = buildTargetUrl(url); const connection = await createGitConnection(targetUrl); await handleGitHandshake(connection); return await handleGitDataTransfer(connection, request); }
async function createGitConnection(url) { const connection = { url: url, socket: null, state: 'connected' }; return connection; }
async function handleGitHandshake(connection) { const handshake = `git-upload-pack ${connection.url.pathname}\0host=${connection.url.hostname}\0`; connection.socket.send(handshake); const response = await connection.socket.receive(); return response; }
async function handleGitDataTransfer(connection, request) { const data = await request.arrayBuffer(); connection.socket.send(data); const response = await connection.socket.receive(); return new Response(response, { headers: { 'Content-Type': 'application/x-git-upload-pack-result' } }); }
|
Git 操作优化
Clone 操作优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| async function optimizeClone(url, options = {}) { const { depth = 0, singleBranch = false, branch = null } = options; let optimizedUrl = convertGitUrl(url); const searchParams = new URLSearchParams(); if (depth > 0) { searchParams.set('depth', depth.toString()); } if (singleBranch) { searchParams.set('single-branch', 'true'); } if (branch) { searchParams.set('branch', branch); } if (searchParams.toString()) { optimizedUrl += `?${searchParams.toString()}`; } return optimizedUrl; }
const optimizedUrl = await optimizeClone('https://github.com/user/repo.git', { depth: 1, singleBranch: true, branch: 'main' });
|
Fetch 操作优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| async function optimizeFetch(url, options = {}) { const { depth = 0, prune = false, tags = true } = options; let optimizedUrl = convertGitUrl(url); const searchParams = new URLSearchParams(); if (depth > 0) { searchParams.set('depth', depth.toString()); } if (prune) { searchParams.set('prune', 'true'); } if (!tags) { searchParams.set('no-tags', 'true'); } if (searchParams.toString()) { optimizedUrl += `?${searchParams.toString()}`; } return optimizedUrl; }
const optimizedUrl = await optimizeFetch('https://github.com/user/repo.git', { depth: 1, prune: true, tags: false });
|
Push 操作优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| async function optimizePush(url, options = {}) { const { force = false, forceWithLease = false, tags = false } = options; let optimizedUrl = convertGitUrl(url); const searchParams = new URLSearchParams(); if (force) { searchParams.set('force', 'true'); } if (forceWithLease) { searchParams.set('force-with-lease', 'true'); } if (tags) { searchParams.set('tags', 'true'); } if (searchParams.toString()) { optimizedUrl += `?${searchParams.toString()}`; } return optimizedUrl; }
const optimizedUrl = await optimizePush('https://github.com/user/repo.git', { force: false, tags: true });
|
Git 配置优化
全局配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| git config --global url."https://xget.xi-xu.me/gh/".insteadOf "https://github.com/" git config --global url."https://xget.xi-xu.me/gl/".insteadOf "https://gitlab.com/" git config --global url."https://xget.xi-xu.me/gitea/".insteadOf "https://gitea.com/"
git config --global http.proxy http://xget.xi-xu.me:8080 git config --global https.proxy https://xget.xi-xu.me:8443
git config --global http.cache 3600 git config --global https.cache 3600
git config --global core.compression 9 git config --global http.compression 9 git config --global https.com 9
|
仓库配置
1 2 3 4 5 6 7 8 9 10
| cd your-repo
git config url."https://xget.xi-xu.me/gh/".insteadOf "https://github.com/"
git config lfs.url https://xget.xi-xu.me/gh/user/repo.git/info/lfs git config lfs.basictransfersonly true git config lfs.concurrenttransfers 8
|
性能配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| git config --global http.postBuffer 524288000 git config --global http.lowSpeedLimit 0 git config --global http.lowSpeedTime 999999
git config --global http.timeout 600 git config --global http.connectTimeout 30 git config --global http.readTimeout 600
git config --global http.maxRetries 3 git config --global http.retryDelay 1000
|
常见问题
Q1: 如何验证 Git 加速是否生效?
A: 使用 time 命令测量操作时间:
1 2 3 4 5
| time git clone https://xget.xi-xu.me/gh/user/repo.git
time git fetch origin
|
Q2: Git LFS 如何使用 Xget 加速?
A: 配置 LFS 使用 Xget:
1 2
| git config lfs.url https://xget.xi-xu.me/gh/user/repo.git/info/lfs git lfs fetch
|
Q3: 如何处理 Git 认证问题?
A: 使用 Git 凭据存储:
1 2 3 4 5
| git config --global credential.helper store
git push
|
Q4: Xget 支持哪些 Git 操作?
A: Xget 支持所有 Git 操作,包括 clone、fetch、pull、push、LFS 等。
Q5: 如何切换回原始 Git URL?
A: 移除 Git 配置:
1
| git config --global --unset url."https://xget.xi-xu.me/gh/".insteadOf
|
总结
Xget 提供了全面的 Git 协议支持,包括 HTTP/HTTPS、SSH、Git 协议等各种传输协议。通过 URL 转换、请求代理、响应缓存等技术,Xget 显著提升了 Git 操作的速度和稳定性。
同时,Xget 对 Git LFS、Submodule 等高级功能也提供了完善的支持,确保开发者能够享受到完整的 Git 加速体验。