跳到主要内容

远程仓库

本章将深入介绍如何连接远程仓库,以及推送和拉取代码。理解远程仓库的工作机制是团队协作的基础。

什么是远程仓库?

远程仓库是托管在网络上的项目版本库,允许多人协作开发。每个开发者本地都有一个完整的仓库副本,通过推送(push)和拉取(pull/clone)与远程仓库同步。

本地与远程的关系

Git 是分布式版本控制系统,这意味着每个开发者的本地仓库都是完整的,包含所有的历史记录和版本信息。远程仓库的作用是:

  1. 作为协作中心:多个开发者通过远程仓库同步代码
  2. 作为备份:代码存储在远程服务器上,避免本地丢失
  3. 作为权威来源:持续集成、部署通常基于远程仓库

远程仓库名称约定

  • 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 fetchgit pull 时才会更新。

跟踪分支

跟踪分支是设置了上游关系的本地分支。设置跟踪后,可以省略 git pullgit 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>设置上游分支

小结

本章我们学习了:

  1. 远程仓库的概念:分布式版本控制中远程仓库的作用
  2. 查看和管理远程仓库:remote 命令的各种用法
  3. 获取和推送数据:fetch、pull、push 的区别和使用场景
  4. 远程分支:跟踪分支、远程跟踪分支的概念
  5. Refspec:本地与远程分支的映射关系
  6. 克隆选项:浅克隆、单分支克隆等
  7. 协议选择:HTTPS、SSH 等协议的特点
  8. 协作工作流:功能分支和 Fork 工作流

练习

  1. 创建一个 GitHub 仓库,使用不同协议克隆
  2. 添加多个远程仓库,尝试在不同远程间推送
  3. 练习使用 git remote show 查看远程仓库详细信息
  4. 设置分支跟踪关系,体验省略参数的 push/pull
  5. 尝试浅克隆并转换为完整克隆
  6. 使用 Fork 工作流参与一个开源项目

参考资料