Git 远程协作指南
指令速览
| 指令 | 作用 |
|---|---|
git clone 仓库地址 | 克隆远程仓库到本地 |
git remote -v | 查看已配置的远程仓库 |
git remote add 名称 地址 | 添加远程仓库 |
git remote remove 名称 | 删除远程仓库 |
git remote rename 旧名 新名 | 重命名远程仓库 |
git remote set-url 名称 新地址 | 修改远程仓库地址 |
git branch | 查看本地分支 |
git branch -a | 查看所有分支(含远程) |
git checkout -b 分支名 | 创建并切换到新分支 |
git checkout 分支名 | 切换到指定分支 |
git push -u origin 分支名 | 首次推送分支并建立追踪关系 |
git push | 推送当前分支到远程 |
git fetch | 拉取远程更新到远程跟踪分支 |
git pull | 拉取远程更新并合并到当前分支 |
git merge 分支名 | 合并指定分支到当前分支 |
git merge --no-ff 分支名 | 非快进合并,保留分支历史 |
git merge --abort | 放弃本次合并 |
git status | 查看分支状态及与远程的差异 |
本栏目将介绍如何使用 Git 进行远程协作,也就是多人共享一个远程仓库,大家各自在本地写代码,然后把版本同步到远程,再从远程同步回本地
远程协作的仓库结构
远程协作本质上是下列分支的更新、切换和合并:
-
本地分支 例如
main、dev、feature/login,你在本地git checkout或git switch切换的是它 -
远程跟踪分支 例如
origin/main、origin/dev,它代表你上一次从远程看到的分支状态 即:它是一个只读的参照物,用来告诉你远程有什么分支 -
远程仓库名 最常见叫
origin,它不是分支,它是远程地址的别名
你可以把远程协作理解成在本地三大区域之外再加一层:
- 工作区:你实际编码与工作的区域,也就是项目文件夹下除了
.git文件夹之外的所有区域 - 暂存区:存储
git add添加的文件更改信息,用于下一次提交 - 版本库:存储所有提交历史,通过
HEAD指针标识当前版本 - 远程仓库:团队共享的版本库,你通过
git push把本地提交同步过去,通过git fetch或git pull把远程更新同步回来
远程协作的基本工作流程
远程协作的大框架概括来说就是:
- 在工作区中修改文件
- 使用
git add添加改动到暂存区 - 使用
git commit生成新的版本到版本库 - 使用
git push把本地提交同步到远程仓库 - 使用
git fetch或git pull把远程更新(通常是主分支)同步到本地 - 使用
git merge来将最新的主分支的内容合并到本地的自己的开发分支上
分支与指针
在远程协作里,分支是最重要的组织方式
查看当前存在的分支
在 Git Bash 中,当前分支显示在输入提示命令行的最右端,并且使用括号包围着新建的仓库总是处于主分支,分支名为 main(这个在我们之前的配置流程中自定义过)
分支是什么
分支不是一份代码副本分支本质上是一个指针,它指向某一个提交 commit
你可以把提交看成一串串节点:
A --- B --- C --- D
^
main
上面这张图的意思是:main 这个分支名指向提交 D
HEAD 是什么
HEAD 表示你当前在哪个分支上,也就是你当前正在操作的分支
A --- B --- C --- D
^
main
^
HEAD
当你在 main 分支上做一次新的 git commit,发生的事情是:
- Git 生成一个新的版本提交
E main这个指针从D移到EHEAD仍然指向main
A --- B --- C --- D --- E
^
main
^
HEAD
即:分支会跟着你最新的提交向前移动
为什么远程协作一定要用分支
多人协作时,大家都会不断提交你们需要一条稳定的共享线,通常叫 main,也需要每个人单独干活的线,通常叫 feature/xxx 或者 dev-xxx这样做使得:
main保持稳定,大家随时能拉下来运行- 每个人在自己的功能分支上提交,互不影响
- 功能完成后再合并回
main
本地分支、远程跟踪分支、追踪关系
远程协作里,分支名字有两种情况:
本地分支
例如 main、dev、feature/login你在本地切换分支切换的是它
(本地跟踪的)远程分支
例如 origin/main、origin/dev
它的含义是:你本地存储的远程分支记录,也就是你上一次 git fetch 之后看到的远程状态
注意!
origin/main 不是你在远程直接操作的分支,它是你本地的一个参照物你不能直接在 origin/main 上写提交
追踪关系 upstream
追踪关系是本地分支和远程分支的提交与下载关系,使用 git pull 下载,使用 git push 上传
当你的本地分支和远程分支建立追踪关系后:
- 你在
main上执行git pull,Git 知道要从origin/main拉 - 你在
main上执行git push,Git 知道要推到origin/main
追踪关系最常见的建立方式是第一次 push 使用 -u,之后就正常使用 git push 就行:
git push -u origin main
这行命令的意思是说,将本地版本的修改提交到远程仓库(名称为 origin)的 main 分支上
分支的常用命令
git branch:查看本地分支
git branch
你会看到每个本地分支追踪哪个远程分支,以及领先落后情况
git checkout -b:创建并切换到新分支
git checkout -b feature/login
执行后发生的事情是:
- 新建分支
feature/login指向当前提交 HEAD切到feature/login- 工作区内容切换为该分支对应的版本
git checkout:切换分支
git checkout main
注意!
切换分支前先看 git status如果工作区有未提交修改,切分支会把这些修改带过去,容易把逻辑混在一起
git push -u:第一次推送分支到远程并建立追踪关系
git push -u origin feature/login
后续推送分支:
git push
远程仓库管理
在远程协作中,了解如何管理远程仓库是很重要的以下是常用的远程仓库管理命令:
git remote -v:查看已配置的远程仓库
git remote -v
这个命令会显示所有已配置的远程仓库名称及其对应的 URL,例如:
origin git@github.com:username/repo.git (fetch)
origin git@github.com:username/repo.git (push)
git remote add:添加远程仓库
git remote add 远程名称 远程仓库地址
例如:
git remote add origin git@github.com:username/repo.git
即:添加一个名叫 origin 的、地址为 git@github.com:username/repo.git 的远程仓库
什么时候需要添加多个远程?
有时候你可能需要同时关联多个远程仓库,比如:
- 同时推送到 GitHub 和 Gitee(国内镜像)
- fork 了别人的项目后,需要同时关联上游仓库和 fork 的仓库
# 添加上游仓库(用于同步原项目更新)
git remote add upstream git@github.com:original-owner/original-repo.git
git remote remove:删除远程仓库
git remote remove 远程名称
例如:
git remote remove origin
git remote rename:重命名远程仓库
git remote rename 旧名称 新名称
例如:
git remote rename origin github
git remote set-url:修改远程仓库地址
当你需要更换远程仓库地址时(比如从 HTTPS 切换到 SSH,或者仓库迁移了),可以使用这个命令:
git remote set-url 远程名称 新地址
例如:
git remote set-url origin git@github.com:new-username/new-repo.git
Git 远程协作的初始化
远程协作的起点分两种情况,你按自己情况选一种
情况 A:你从远程把仓库克隆下来
git clone:克隆远程仓库到本地
这里的地址记得选择 SSH 而不是 HTTP!
git clone 远程仓库地址
执行后 Git 会做三件事:
- 把远程完整下载到你本地生成一个项目文件夹
- 自动配置一个远程名叫
origin - 自动创建一个本地分支并关联远程默认分支
即:你后续
git pull和git push都可以直接用
情况 B:你本地已经有项目,现在要推到远程
这条路径适用于你已经 git init 过并且已经有提交,接下来把它变成远程协作仓库
git remote add:添加远程仓库地址
git remote add origin 远程仓库地址
即:添加一个名叫 origin 的、地址为"远程仓库地址"的远程仓库
git remote -v:查看远程地址
git remote -v
如果你看到 origin 对应两个地址就说明关联成功了
git push -u:第一次推送并建立追踪关系
git push -u origin main
这里的 -u 很关键,它会建立追踪关系
即:以后你在 main 分支上直接 git push,Git 就知道要推到 origin/main
git branch -M:重命名当前分支(如果你命名错了就有救了)
git branch -M main
拉取与推送的核心命令
git fetch:拉取远程更新到远程跟踪分支
git fetch origin
如果你当前处于 main 分支,执行后变化是:
- 本地的远程分支记录
origin/main变了 - 你的本地
main分支不动 - 你的工作区文件不动
即:fetch 只更新远程分支的情况,让你和你的 Git 先明白远程分支的更新有什么,只有一个参考的作用
git pull:拉取远程更新并合并到本地分支
git pull
pull 等价于两步:
git fetch- 把远程跟踪分支对于工作区的修改合并进当前分支的工作区
即:pull 会直接改你的本地这一分支的工作区,有冲突就会直接进入冲突流程
git push:推送本地提交到远程
git push
注意!
push 推不上去最常见原因是远程比你新,解决方式是先 git pull 把远程更新拿下来再处理,不要用 force push
git status:观察远程与本地分支的差异
git status
你会看到类似信息:
- 当前分支是否落后远程
- 当前分支是否领先远程
- 工作区与暂存区是否干净(在本地管理栏目中有介绍)
git merge:合并一个分支的更新到现在的分支并创建版本
merge 是把两条分支的历史在一个新提交里合起来,形成一个合并提交
合并提交生成后,历史通常长这样:
A --- B --- C --- E -------- M
\ /
D --- F ------ /
M 就是合并提交
merge 的特点是历史会保留真实的分叉与合并,便于追溯团队协作过程
推荐使用 git merge --no-ff
在实际团队协作中,我们强烈推荐使用 --no-ff 参数进行合并:
git merge --no-ff feature/login
什么是 --no-ff?
--no-ff 是 "no fast-forward" 的缩写,意思是"禁止快进合并"
快进合并(Fast-forward) 是指当目标分支(如 main)没有新的提交,而源分支(如 feature/login)有新的提交时,Git 直接把 main 的指针移动到 feature/login 的位置,而不创建新的合并提交历史会变成一条直线:
# 快进合并前
A --- B --- C (main)
\
D --- E (feature/login)
# 快进合并后
A --- B --- C --- D --- E (main, feature/login)
非快进合并(No fast-forward) 会强制创建一个新的合并提交,即使可以快进:
# 非快进合并后
A --- B --- C ----------- F (main)
\ /
D --- E -- (feature/login)
为什么要用 --no-ff?
-
保留分支历史信息:可以清楚地看到哪些提交是属于哪个功能分支的,方便回溯和理解项目历史
-
便于回滚:如果某个功能出了问题,可以直接回滚整个合并提交,而不需要一个个找该功能的提交
-
代码审查更清晰:在 PR/MR 中可以看到完整的功能分支做了哪些更改
-
团队协作更透明:每个功能分支的合并都有明确的记录点,便于项目经理追踪进度
分支协作的标准流程
多人协作的核心规则是:不要动共享分支!
共享分支的含义是团队共同依赖它,最常见就是 main
你做功能就开新分支,做完再合并
标准的合作流程如下:
A:初始化功能分支
1. 创建功能分支
git checkout -b feature/login
2. 推送功能分支到远程
第一次推功能分支需要建立追踪关系:
git push -u origin feature/login
此时远程就会出现 feature/login
B:在本地进行版本迭代
在本地迭代代码并使用 add 和 commit 作版本管理
本地管理里已经强调了 add 和 commit 的关系,这里完全一致,远程协作里更强调两点:
- 一次
commit只做一类事情,例如只做一个修复,只做一个小功能 commit信息写清楚干了什么,例如fix: login crash或feat: add login page
C:提交本地更改到远程仓库
当你认为本次的开发任务已经完成时,你需要提交本地更改到远程仓库
1. 合并主分支的最新内容
原因:可能其他人在你更新自己的功能分支时对于主分支进行了更新,这可能会导致冲突
-
切回主分支并更新到最新
git checkout main
git pull -
切换到自己的功能分支
git checkout feature/login -
合并最新的主分支到功能分支
git merge main或者使用
--no-ff参数(推荐):git merge --no-ff main -
解决冲突(如果执行 merge 后有冲突提示)
2. 处理冲突(如果存在)
远程协作真正麻烦的地方只有一个:冲突
冲突出现的典型场景是:
- 你改了某行
- 别人也改了同一行
- 你执行
git pull或git merge时 Git 无法自动决定用谁的
-
先看状态
git status你会看到哪些文件处于 unmerged 状态
-
打开冲突文件,手工处理冲突标记
冲突标记包含三段边界:
<<<<<<<=======>>>>>>>
你要做的事是删掉这些标记,并把最终想保留的内容整理成正确代码
-
解决完后把文件 add 回暂存区,或者直接
git add .git add 冲突文件路径 -
完成合并提交
git commit
3. 合并失败想撤回本次合并
git merge --abort
这条命令会把仓库状态恢复到你执行 merge 之前
4. 提交本地
确定现在自己位于自己的开发分支,而不是主分支,运行 git push,将远程的自己的功能分支与本地同步
D:提交 PR,告知组长审核
目前的进展是这样的:
- 你已经将远程的你的功能分支和本地功能分支同步了
- 但是远程的主分支却还没有与你的开发分支同步
这个同步不是你来做的,而是组长来做的,你需要在 GitHub 上(也就是远程仓库上)Pull Request 界面提交一个新的 Pull Request,并说明你的功能分支开发了什么内容,并告知组长审核这个 Pull Request
这个 request 的目的是:将你的远程开发分支合并到远程主分支上,将你做的更改与其他开发人员在之后的开发流程中共享
之所以不能直接合并,而是只能提出请求,是因为你开发的新功能还没有经过测试和检验,可能会对主分支的代码造成混淆或者损坏,所以需要项目负责人来审核
当组长审核完毕,你这次的开发任务就正式完成了