远程仓库
本章将深入介绍如何连接远程仓库,以及推送和拉取代码。理解远程仓库的工作机制是团队协作的基础。
什么是远程仓库?
远程仓库是托管在网络上的项目版本库,允许多人协作开发。每个开发者本地都有一个完整的仓库副本,通过推送(push)和拉取(pull/clone)与远程仓库同步。
本地与远程的关系
Git 是分布式版本控制系统,这意味着每个开发者的本地仓库都是完整的,包含所有的历史记录和版本信息。远程仓库的作用是:
- 作为协作中心:多个开发者通过远程仓库同步代码
- 作为备份:代码存储在远程服务器上,避免本地丢失
- 作为权威来源:持续集成、部署通常基于远程仓库
远程仓库名称约定
origin:默认的远程仓库名称,通常是你克隆的来源仓库upstream:上游仓库,常见于 Fork 工作流中,指原始仓库fork或个人用户名:个人 Fork 的仓库
查看远程仓库
基本查看
# 查看远程仓库列表
git remote
# 输出示例
origin
# 查看详细信息(包括URL)
git remote -v
# 输出示例
origin https://github.com/username/repository.git (fetch)
origin https://github.com/username/repository.git (push)
输出中 (fetch) 和 (push) 分别表示用于拉取和推送的 URL。大多数情况下它们相同,但也可以配置不同的 URL。
查看详细信息
git remote show 命令可以查看某个远程仓库的详细信息:
# 查看远程仓库的详细信息
git remote show origin
输出示例:
* remote origin
Fetch URL: https://github.com/username/repository.git
Push URL: https://github.com/username/repository.git
HEAD branch: main
Remote branches:
main tracked
develop tracked
feature/login tracked
refs/remotes/origin/release stale (use 'git remote prune' to remove)
Local branches configured for 'git pull':
main merges with remote main
develop merges with remote develop
Local refs configured for 'git push':
main pushes to main (up to date)
develop pushes to develop (local out of date)
这个命令显示了很多有用信息:
- Fetch/Push URL:拉取和推送的地址
- HEAD branch:远程仓库的默认分支
- Remote branches:远程分支的状态
tracked:正在跟踪的分支stale:远程已删除但本地还有记录的分支
- Local branches configured for 'git pull':运行
git pull时会自动合并的分支关系 - Local refs configured for 'git push':运行
git push时的推送配置
添加远程仓库
克隆时自动添加
当你克隆仓库时,Git 会自动添加名为 origin 的远程仓库:
git clone https://github.com/username/repository.git
# Git 自动执行:git remote add origin https://github.com/username/repository.git
手动添加远程仓库
# 基本语法
git remote add <shortname> <url>
# 添加远程仓库
git remote add origin https://github.com/username/repository.git
# 添加并立即获取数据
git remote add -f origin https://github.com/username/repository.git
添加多个远程仓库
一个项目可以有多个远程仓库:
# 添加上游仓库(Fork 工作流常用)
git remote add upstream https://github.com/original-owner/repository.git
# 添加个人 Fork
git remote add myfork https://github.com/yourusername/repository.git
# 添加镜像仓库
git remote add mirror https://mirror.example.com/repository.git
# 查看所有远程仓库
git remote -v
输出示例:
origin https://github.com/yourusername/repository.git (fetch)
origin https://github.com/yourusername/repository.git (push)
upstream https://github.com/original-owner/repository.git (fetch)
upstream https://github.com/original-owner/repository.git (push)
myfork https://github.com/yourusername/repository.git (fetch)
myfork https://github.com/yourusername/repository.git (push)
管理远程仓库
重命名远程仓库
# 重命名远程仓库
git remote rename old-name new-name
# 示例:将 pb 重命名为 paul
git remote rename pb paul
重命名后,Git 会自动更新相关的远程跟踪分支名称。原来 pb/main 会变成 paul/main。
修改远程仓库 URL
# 修改远程仓库 URL
git remote set-url origin https://github.com/username/new-repository.git
# 使用不同的推送和拉取 URL
git remote set-url origin https://github.com/username/repository.git
git remote set-url --push origin [email protected]:username/repository.git
# 查看修改后的结果
git remote -v
删除远程仓库
# 删除远程仓库
git remote remove <name>
# 或
git remote rm <name>
# 示例
git remote remove paul
删除远程仓库后,所有相关的远程跟踪分支和配置也会被删除。
清理过期的远程分支引用
当远程分支被删除后,本地可能还保留着对它的引用:
# 查看过期的远程分支引用
git remote show origin
# 会显示 stale (use 'git remote prune' to remove)
# 清理单个远程仓库的过期引用
git remote prune origin
# 在获取时自动清理
git fetch -p
# 或
git fetch --prune
# 清理所有远程仓库的过期引用
git remote update --prune
获取远程数据
git fetch:获取但不合并
git fetch 从远程仓库获取最新的数据,但不会自动合并到当前分支:
# 获取 origin 的所有更新
git fetch origin
# 获取特定分支的更新
git fetch origin main
# 获取所有远程仓库的更新
git fetch --all
# 获取时清理过期的远程分支引用
git fetch -p
fetch 过程示意图:
关键理解:fetch 只更新远程跟踪分支(如 origin/main),不会改变你的本地分支。这让你可以先查看远程的更新,再决定是否合并。
fetch 后,你可以:
# 查看远程分支的更新
git log HEAD..origin/main
# 查看差异
git diff HEAD..origin/main
# 合并远程更新
git merge origin/main
# 或使用变基
git rebase origin/main
git pull:获取并合并
git pull 等于 git fetch + git merge:
# 基本用法
git pull origin main
# 如果当前分支设置了上游分支,可以省略参数
git pull
pull 过程示意图:
pull 和 fetch 的选择
| 场景 | 推荐命令 | 原因 |
|---|---|---|
| 想先查看远程更新 | git fetch | 可以先检查再决定是否合并 |
| 确定要直接合并 | git pull | 一步完成 |
| 需要保持线性历史 | git fetch + git rebase | 可以选择变基方式合并 |
| 团队协作中 | git fetch | 更安全,避免意外合并 |
pull 的变基模式
使用 git pull --rebase 可以保持线性历史:
# 使用变基方式拉取
git pull --rebase origin main
# 配置默认使用变基
git config pull.rebase true
或者配置特定分支:
# 配置 main 分支使用变基
git config branch.main.rebase true
推送代码
基本推送
# 推送到远程仓库
git push origin main
# 推送当前分支(需要先设置上游分支)
git push
# 首次推送并设置上游分支
git push -u origin main
# 或
git push --set-upstream origin main
推送过程示意图:
推送分支
# 推送本地分支到远程
git push origin feature-login
# 推送并设置上游分支
git push -u origin feature-login
# 推送本地分支到远程的不同名称
git push origin local-branch:remote-branch
# 推送所有分支
git push origin --all
# 推送所有分支和标签
git push origin --all --tags
强制推送
当本地历史与远程不一致时,可能需要强制推送:
# 强制推送(危险操作)
git push --force origin main
# 更安全的强制推送
git push --force-with-lease origin main
--force-with-lease 的优势:它会检查远程分支是否还是你预期的状态。如果远程有其他人推送了新提交,推送会被拒绝,避免意外覆盖别人的工作。
# 带远程引用的强制推送
git push --force-with-lease=main:origin/main origin main
推送被拒绝的处理
当推送被拒绝时(远程有新提交):
# 方案一:拉取合并后推送(最常用)
git pull origin main
# 解决可能的冲突
git push origin main
# 方案二:拉取变基后推送(保持线性历史)
git pull --rebase origin main
# 解决可能的冲突
git rebase --continue # 如果有冲突
git push origin main
# 方案三:强制推送(仅在你确定安全时使用)
git push --force-with-lease origin main
远程分支
远程跟踪分支
远程跟踪分支是远程分支状态的本地引用,它们以 <remote>/<branch> 的形式命名:
# 查看远程分支
git branch -r
# 输出示例
origin/HEAD -> origin/main
origin/main
origin/develop
origin/feature-login
# 查看所有分支(本地+远程)
git branch -a
重要理解:远程跟踪分支(如 origin/main)是只读的。Git 不会自动更新它们,只有在执行 git fetch 或 git pull 时才会更新。
跟踪分支
跟踪分支是设置了上游关系的本地分支。设置跟踪后,可以省略 git pull 和 git push 的参数:
# 创建跟踪分支
git checkout -b feature-login origin/feature-login
# 或简写
git checkout --track origin/feature-login
# 更简洁的方式(分支名相同时)
git checkout feature-login
# 为已存在的分支设置上游
git branch -u origin/feature-login
# 或
git branch --set-upstream-to=origin/feature-login
# 查看分支的跟踪关系
git branch -vv
# 输出示例
main abc1234 [origin/main] 最新提交信息
* develop def5678 [origin/develop] 最新提交信息
feature ghi9012 本地分支,无上游
删除远程分支
# 删除远程分支
git push origin --delete feature-login
# 旧语法(不推荐)
git push origin :feature-login
Refspec 详解
Refspec 定义了本地分支与远程分支的映射关系,这是理解 Git 远程操作的关键概念。
基本格式
Refspec 的格式是 [+]源引用:目标引用:
+:可选,表示允许强制更新(非快进)- 源引用:本地或远程的分支/引用
- 目标引用:对应的远程或本地分支/引用
查看 Refspec
# 查看远程仓库的配置
cat .git/config
# 输出示例
[remote "origin"]
url = https://github.com/username/repository.git
fetch = +refs/heads/*:refs/remotes/origin/*
这行配置表示:
refs/heads/*:远程的所有分支refs/remotes/origin/*:本地的远程跟踪分支+:允许强制更新
自定义 Refspec
# 只获取 main 分支
git fetch origin refs/heads/main:refs/remotes/origin/main
# 获取特定分支到不同的本地分支名
git fetch origin refs/heads/main:refs/remotes/origin/production
# 推送时指定 refspec
git push origin refs/heads/main:refs/heads/production
# 简写形式
git push origin main:production
实际应用场景
场景:从其他仓库获取特定分支
# 添加另一个远程仓库
git remote add other https://github.com/other/repo.git
# 只获取它的 main 分支到本地的 other-main 分支
git fetch other main:other-main
场景:推送多个分支
# 推送 main 和 develop
git push origin main develop
# 使用通配符推送所有 feature 分支
git push origin refs/heads/feature/*:refs/heads/feature/*
克隆仓库
基本克隆
# 克隆仓库
git clone https://github.com/username/repository.git
# 克隆到指定目录
git clone https://github.com/username/repository.git my-project
# 克隆指定分支
git clone -b develop https://github.com/username/repository.git
# 克隆指定分支并指定目录
git clone -b develop https://github.com/username/repository.git my-project
浅克隆
对于大型仓库,可以使用浅克隆只获取最近的提交:
# 只克隆最近一次提交
git clone --depth 1 https://github.com/username/repository.git
# 只克隆最近的 n 次提交
git clone --depth 10 https://github.com/username/repository.git
# 只克隆特定分支的浅克隆
git clone --depth 1 --branch main https://github.com/username/repository.git
浅克隆的优点:
- 克隆速度快
- 占用空间小
浅克隆的缺点:
- 没有完整的历史记录
- 某些操作(如 blame)可能不完整
- 不能轻易转换为完整克隆
# 将浅克隆转换为完整克隆
git fetch --unshallow
单分支克隆
只克隆一个分支:
# 只克隆 main 分支
git clone --single-branch --branch main https://github.com/username/repository.git
递归克隆(含子模块)
如果仓库包含子模块,需要递归克隆:
# 递归克隆
git clone --recursive https://github.com/username/repository.git
# 如果已经克隆,初始化子模块
git submodule update --init --recursive
协议选择
Git 支持多种协议访问远程仓库:
HTTPS
git clone https://github.com/username/repository.git
优点:
- 配置简单
- 防火墙友好
- 支持凭证缓存
缺点:
- 每次推送需要认证(可配置凭证缓存解决)
SSH
git clone [email protected]:username/repository.git
优点:
- 一次配置,无需重复认证
- 更安全
- 支持部署密钥
缺点:
- 需要配置 SSH 密钥
- 某些网络环境可能屏蔽 SSH 端口
Git 协议
git clone git://github.com/username/repository.git
优点:
- 速度最快
缺点:
- 没有认证机制
- 只适合公开仓库
- 很多网络环境屏蔽
协议切换
# 从 HTTPS 切换到 SSH
git remote set-url origin [email protected]:username/repository.git
# 从 SSH 切换到 HTTPS
git remote set-url origin https://github.com/username/repository.git
多人协作工作流
功能分支工作流
# 1. 确保主分支是最新的
git checkout main
git pull origin main
# 2. 创建功能分支
git checkout -b feature/new-feature
# 3. 开发并提交
git add .
git commit -m "feat: 添加新功能"
# 4. 保持与主分支同步
git fetch origin
git rebase origin/main
# 5. 推送功能分支
git push -u origin feature/new-feature
# 6. 创建 Pull Request(在 GitHub/GitLab 上)
# 7. 合并后清理本地分支
git checkout main
git pull origin main
git branch -d feature/new-feature
Fork 工作流
适用于开源项目或需要严格控制权限的项目:
# 1. Fork 仓库(在 GitHub 上操作)
# 2. 克隆自己的 Fork
git clone https://github.com/your-username/repository.git
cd repository
# 3. 添加上游仓库
git remote add upstream https://github.com/original-owner/repository.git
# 4. 创建功能分支
git checkout -b feature/new-feature
# 5. 开发并提交
git add .
git commit -m "feat: 添加新功能"
# 6. 保持与上游同步
git fetch upstream
git rebase upstream/main
# 7. 推送到自己的 Fork
git push origin feature/new-feature
# 8. 创建 Pull Request 到上游仓库
# 9. 合并后同步上游
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
常见问题处理
推送被拒绝
# 错误信息
! [rejected] main -> main (non-fast-forward)
# 原因:远程有本地没有的提交
# 解决方案一:先拉取再推送
git pull --rebase origin main
git push origin main
# 解决方案二:强制推送(谨慎使用)
git push --force-with-lease origin main
认证失败
# HTTPS 认证失败
# 解决方案一:使用 Personal Access Token(GitHub)
# 在 Settings -> Developer settings -> Personal access tokens 创建
# 推送时用户名填 GitHub 用户名,密码填 Token
# 解决方案二:配置凭证缓存
git config --global credential.helper store # 永久存储
git config --global credential.helper cache # 临时缓存(默认15分钟)
git config --global credential.helper 'cache --timeout=3600' # 缓存1小时
# 解决方案三:切换到 SSH
git remote set-url origin [email protected]:username/repository.git
分支同步问题
# 本地分支落后于远程
git checkout main
git pull origin main
# 本地分支有未推送的提交
git push origin main
# 分支历史不一致
git fetch origin
git reset --hard origin/main # 放弃本地修改
# 或
git rebase origin/main # 变基到远程
命令速查表
| 命令 | 说明 |
|---|---|
git remote -v | 查看远程仓库详细信息 |
git remote add <name> <url> | 添加远程仓库 |
git remote show <name> | 查看远程仓库详细信息 |
git remote rename <old> <new> | 重命名远程仓库 |
git remote set-url <name> <url> | 修改远程仓库 URL |
git remote remove <name> | 删除远程仓库 |
git remote prune <name> | 清理过期的远程分支引用 |
git fetch <remote> | 获取远程更新(不合并) |
git pull <remote> <branch> | 获取并合并远程更新 |
git push <remote> <branch> | 推送到远程 |
git push -u <remote> <branch> | 推送并设置上游分支 |
git push --force-with-lease | 安全强制推送 |
git clone <url> | 克隆仓库 |
git clone --depth 1 <url> | 浅克隆 |
git branch -r | 查看远程分支 |
git branch -vv | 查看分支跟踪关系 |
git branch -u <remote>/<branch> | 设置上游分支 |
小结
本章我们学习了:
- 远程仓库的概念:分布式版本控制中远程仓库的作用
- 查看和管理远程仓库:remote 命令的各种用法
- 获取和推送数据:fetch、pull、push 的区别和使用场景
- 远程分支:跟踪分支、远程跟踪分支的概念
- Refspec:本地与远程分支的映射关系
- 克隆选项:浅克隆、单分支克隆等
- 协议选择:HTTPS、SSH 等协议的特点
- 协作工作流:功能分支和 Fork 工作流
练习
- 创建一个 GitHub 仓库,使用不同协议克隆
- 添加多个远程仓库,尝试在不同远程间推送
- 练习使用
git remote show查看远程仓库详细信息 - 设置分支跟踪关系,体验省略参数的 push/pull
- 尝试浅克隆并转换为完整克隆
- 使用 Fork 工作流参与一个开源项目