📦 第03章 · 远程仓库与协作

clone push pull remote fetch

远程仓库与协作

前两章都是单人、本地操作。真正干活时,你需要一个"中心仓库"让大家把代码汇到一处、再从一处取——这就是远程仓库 (remote)。在我们的环境里,它就是自托管的 GitLab gitlab.bangdream.xyz。这一章讲 clone、remote、fetch、pull、push——协作的五块基石。

远程仓库是什么

远程仓库本质上是一个存储在网络另一端的 Git 仓库。本地仓库里存着一份"远程地址的别名"(origin),指向某个 URL。你可以有多个远程:origin 通常指你的主要 GitLab 仓库,还可以再加 upstream、fork 等。

git remote           # 列出远程别名
git remote -v        # 列出别名 + 实际 URL(最常用)

在 GitLab 上新建项目后,它会给你两种克隆地址:

协议 示例 特点
HTTPS https://gitlab.bangdream.xyz/group/app.git 用用户名+访问令牌认证,适合浏览器/脚本
SSH git@gitlab.bangdream.xyz:group/app.git 用 SSH 密钥认证,免密、更安全,适合服务器

可以从 gitlab-rake gitlab:env:info(第 4 章)里看到本机 GitLab 生成的克隆地址,我们的项目大概长这样:

HTTP Clone URL: https://gitlab.bangdream.xyz/group/app.git
SSH  Clone URL: git@gitlab.bangdream.xyz:group/app.git

git clone:一次性拉到本地

clone 把远程仓库完整克隆一份到本地,自动建一对默认远程别名 origin,并检出默认分支:

git clone https://gitlab.bangdream.xyz/group/app.git ~/app     # HTTPS
git clone git@gitlab.bangdream.xyz:group/app.git ~/app         # SSH
git clone --depth 1 <url>                                       # 只取最近一次提交(浅克隆)
git clone --branch feature/x <url>                              # 克隆后直接进指定分支

克隆后 git remote -v 已经指向目标仓库了。注意 clone 拉的是仓库的全部历史,初始体积可能不小;--depth 1 可显著减小,但会牺牲完整历史。

易错 clone 的网址如果敲错或权限不足,会报 fatal: repository not found 或 Authentication failed。先确认 URL、再确认你有该仓库的读权限(第 6 章讲权限),许多"克隆失败"其实是账号没有权限。

认证方式:HTTPS 令牌 vs SSH 密钥

直接推送到远程必须证明身份。两种主流方式:

HTTPS 令牌——在 GitLab 个人设置 → 访问令牌 里生成一个 token,克隆/推送时填入。令牌比密码可控(可设过期、可限定 read_repository/write_repository 范围),比明文密码安全得多。

# 用令牌 URL 克隆,避免了把令牌写进命令历史
git clone https://oauth2:<令牌>@gitlab.bangdream.xyz/group/app.git

SSH 密钥——在服务器生成一对密钥,把公钥贴到 GitLab 的 SSH Keys 里。之后 git@... 的形式自动免密:

ssh-keygen -t ed25519 -C "deploy@bangdream"   # 生成密钥,按三次回车默认存 ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub                     # 把公钥内容复制到 GitLab → Preferences → SSH Keys
ssh -T git@gitlab.bangdream.xyz               # 测试连通,看到 "Welcome to GitLab, @deploy" 即成功
git clone git@gitlab.bangdream.xyz:group/app.git ~/app

安全 不要把 SSH 私钥(id_ed25519)或 HTTPS 令牌提交进仓库、也别放进 .gitignore 之外会被推送的文件。部署服务器上用它做只读克隆时,建议用受限的部署令牌,而不是开发者个人密钥。

git remote:增删改远程别名

命令 作用
git remote add origin <url> 为远程 URL 起别名
git remote set-url origin <url> 改远程地址(如从 HTTPS 换成 SSH)
git remote remove origin 移除远程别名
git remote rename old new 重命名别名
git remote show origin 查看远程详细信息(分支、tracking)
# 本地已有仓库,想接到 GitLab 上
git remote add origin git@gitlab.bangdream.xyz:group/app.git
git remote -v
# 若原来接的 https,想改 ssh
git remote set-url origin git@gitlab.bangdream.xyz:group/app.git

git fetch:只"拉取"不下手

fetch 把你的本地仓库更新到网络上别人最新提交的状态,但它不改变你当前的工作区与分支,只是把远程新提交下载到本地(origin/main 这类"远程跟踪分支")。它让你先看看远程有什么,再决定怎么整合。

git fetch origin                # 下载 origin 上所有分支的最新提交
git fetch --all                 # 拉取所有远程
git fetch --prune origin        # 同时删除本地已消失的远程分支引用

fetch 之后,git log origin/main 就能看到远程的领先状态,但你的 main 工作区纹丝不动。

git pull:fetch 之后再整合

pull 是 fetch + 合并(默认 merge)的组合拳,一步把远程新提交合并到你当前分支:

git pull origin main            # 拉取并合并到当前分支
git pull --rebase origin main   # 拉取并"变基"到最新,保持线性(推荐)
git push -u origin main         # 首次推送并建立跟踪关系(见下)

fetch 与 pull 的区别一句话

fetch 是下载看到,pull 是下载并整合。多人协作时,想先看看远程改了什么再决定,用 fetch;想直接同步最新,用 pull。初学时建议多用 git fetch && git log origin/main 观察,少直接 pull,避免不明来源的合并。

跟踪分支与上游 (upstream)

执行 git clone 或 git push -u 后,本地分支会记录一个"上游 (upstream)"——即它和远程哪个分支结对。这样直接 git pull、git push 就不用每次带 origin main。

git branch -vv        # 查看本地分支与上游的对应关系
git push -u origin feature/x   # 首次推送并设为上游
git push              # 之后可直接简写

git push:把你本地提交推上去

push 把本地已提交的改动上传到远程。注意 push 的是分支,不是工作区未提交的改动。

git push origin main             # 推送当前分支到 origin 的 main
git push -u origin feature/x     # 首次推送并建立跟踪
git push origin --delete feature/x   # 删除远程分支
git push                          # 有上游时简写
git push --force                   # 强制覆盖远程(危险!)

安全 当你的本地分支和远程分叉(各自有新提交,即历史已改写,比如本地 rebase 过)时,git push 会被拒绝(non-fast-forward)。此时不要动不动 git push --force,除非你确定要覆盖远程且无他人依赖。这是一条极易引发团队事故的规则。

案例场景:从 GitLab 拉取并推送一个前端应用

在本服务器上,一个 Node 前端应用常驻 /var/www/<name>。用 git 做"拉取式"部署非常简单——但注意部署机一般只读地抓取,不要反向推送:

# 1. 在部署机上首次克隆(用部署 SSH 密钥,只读)
git clone git@gitlab.bangdream.xyz:group/webapp.git /var/www/bangdream
cd /var/www/bangdream

# 2. 日常工作:先拉最新再改
git switch main
git pull --rebase               # 变基拉取,保持线性

# 3. 新建分支开发
git switch -c feature/login
# ...改代码、commit...

# 4. 推送到 GitLab
git push -u origin feature/login
# 之后在 GitLab 网页上发起 Merge Request,评审后合入 main

# 5. 拉最新并部署(第 7 章常与 Runner 结合)
git switch main
git pull --rebase origin main
npm ci && npm run build          # 构建前端

协作的正确姿势:分支 + 合并请求

多人协作最稳妥的模式是"分支 + 合并请求":

  1. 每个人都从主线拉分支开发;
  2. 推送到 GitLab 后在网页上发起 Merge Request (MR);
  3. 评审人审核代码、跑 CI(第 7 章),再合并到 main;
  4. 合入后,main 上的提交由 GitLab Runner 自动构建部署。

这样核心主干受保护,谁都不能直接乱推,出问题有据可查。

延伸:更常用的协作工具

  • git fetch --prune + git branch -r:清理本地已删除的远程分支引用。
  • git tag:给特定提交打版本标签(如 v1.2.0),git push origin v1.2.0 上传标签,GitLab 上可据此生成 Release。
  • git remote show origin:查看远程分支、tracking、各分支领先/落后状态,排障利器。
  • git log origin/main..main:看本地比远程多出哪些提交,推送前先自查。

易错点

  • git pull 从错误的远程/分支拉:确认当前分支与上游,git branch -vv 看清再 pull,否则会把别条分支的代码合进来。
  • 明文令牌/私钥进命令历史:别把密码、token 直接写进 git clone https://user:pass@...。用 oauth2:令牌@ 也尽量在受控环境,或改用 SSH 密钥。
  • push 前不看 git log origin/main..main:会把自己的提交一股脑推上去,可能覆盖部署分支。
  • 把代码推错远程:一台服务器上可能挂了多个项目(如 /var/www/bangdream、/var/www/dsh),先 git remote -v 确认 origin 指向目标 GitLab 项目,再 push。

相关命令

git clone、git remote、git fetch、git pull、git push、git tag、ssh-keygen、git branch -vv、git log <a>..<b>。