Skip to main content

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 进行远程协作,也就是多人共享一个远程仓库,大家各自在本地写代码,然后把版本同步到远程,再从远程同步回本地


远程协作的仓库结构

远程协作本质上是下列分支的更新、切换和合并:

  1. 本地分支 例如 maindevfeature/login,你在本地 git checkoutgit switch 切换的是它

  2. 远程跟踪分支 例如 origin/mainorigin/dev,它代表你上一次从远程看到的分支状态 即:它是一个只读的参照物,用来告诉你远程有什么分支

  3. 远程仓库名 最常见叫 origin,它不是分支,它是远程地址的别名

你可以把远程协作理解成在本地三大区域之外再加一层:

  1. 工作区:你实际编码与工作的区域,也就是项目文件夹下除了 .git 文件夹之外的所有区域
  2. 暂存区:存储 git add 添加的文件更改信息,用于下一次提交
  3. 版本库:存储所有提交历史,通过 HEAD 指针标识当前版本
  4. 远程仓库:团队共享的版本库,你通过 git push 把本地提交同步过去,通过 git fetchgit pull 把远程更新同步回来

远程协作的基本工作流程

远程协作的大框架概括来说就是:

  • 工作区中修改文件
  • 使用 git add 添加改动到暂存区
  • 使用 git commit 生成新的版本到版本库
  • 使用 git push 把本地提交同步到远程仓库
  • 使用 git fetchgit 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,发生的事情是:

  1. Git 生成一个新的版本提交 E
  2. main 这个指针从 D 移到 E
  3. HEAD 仍然指向 main
A --- B --- C --- D --- E
^
main
^
HEAD

即:分支会跟着你最新的提交向前移动

为什么远程协作一定要用分支

多人协作时,大家都会不断提交你们需要一条稳定的共享线,通常叫 main,也需要每个人单独干活的线,通常叫 feature/xxx 或者 dev-xxx这样做使得:

  • main 保持稳定,大家随时能拉下来运行
  • 每个人在自己的功能分支上提交,互不影响
  • 功能完成后再合并回 main

本地分支、远程跟踪分支、追踪关系

远程协作里,分支名字有两种情况:

本地分支

例如 maindevfeature/login你在本地切换分支切换的是它

(本地跟踪的)远程分支

例如 origin/mainorigin/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

执行后发生的事情是:

  1. 新建分支 feature/login 指向当前提交
  2. HEAD 切到 feature/login
  3. 工作区内容切换为该分支对应的版本

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 会做三件事:

  1. 把远程完整下载到你本地生成一个项目文件夹
  2. 自动配置一个远程名叫 origin
  3. 自动创建一个本地分支并关联远程默认分支 即:你后续 git pullgit 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 等价于两步:

  1. git fetch
  2. 把远程跟踪分支对于工作区的修改合并进当前分支的工作区

即: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

  1. 保留分支历史信息:可以清楚地看到哪些提交是属于哪个功能分支的,方便回溯和理解项目历史

  2. 便于回滚:如果某个功能出了问题,可以直接回滚整个合并提交,而不需要一个个找该功能的提交

  3. 代码审查更清晰:在 PR/MR 中可以看到完整的功能分支做了哪些更改

  4. 团队协作更透明:每个功能分支的合并都有明确的记录点,便于项目经理追踪进度


分支协作的标准流程

多人协作的核心规则是:不要动共享分支!

共享分支的含义是团队共同依赖它,最常见就是 main 你做功能就开新分支,做完再合并

标准的合作流程如下:


A:初始化功能分支

1. 创建功能分支

git checkout -b feature/login

2. 推送功能分支到远程

第一次推功能分支需要建立追踪关系:

git push -u origin feature/login

此时远程就会出现 feature/login


B:在本地进行版本迭代

在本地迭代代码并使用 add 和 commit 作版本管理

本地管理里已经强调了 addcommit 的关系,这里完全一致,远程协作里更强调两点:

  • 一次 commit 只做一类事情,例如只做一个修复,只做一个小功能
  • commit 信息写清楚干了什么,例如 fix: login crashfeat: add login page

C:提交本地更改到远程仓库

当你认为本次的开发任务已经完成时,你需要提交本地更改到远程仓库

1. 合并主分支的最新内容

原因:可能其他人在你更新自己的功能分支时对于主分支进行了更新,这可能会导致冲突

  1. 切回主分支并更新到最新

    git checkout main
    git pull
  2. 切换到自己的功能分支

    git checkout feature/login
  3. 合并最新的主分支到功能分支

    git merge main

    或者使用 --no-ff 参数(推荐):

    git merge --no-ff main
  4. 解决冲突(如果执行 merge 后有冲突提示)

2. 处理冲突(如果存在)

远程协作真正麻烦的地方只有一个:冲突

冲突出现的典型场景是:

  • 你改了某行
  • 别人也改了同一行
  • 你执行 git pullgit merge 时 Git 无法自动决定用谁的
  1. 先看状态

    git status

    你会看到哪些文件处于 unmerged 状态

  2. 打开冲突文件,手工处理冲突标记

    冲突标记包含三段边界:

    • <<<<<<<
    • =======
    • >>>>>>>

    你要做的事是删掉这些标记,并把最终想保留的内容整理成正确代码

  3. 解决完后把文件 add 回暂存区,或者直接 git add .

    git add 冲突文件路径
  4. 完成合并提交

    git commit

3. 合并失败想撤回本次合并

git merge --abort

这条命令会把仓库状态恢复到你执行 merge 之前

4. 提交本地

确定现在自己位于自己的开发分支,而不是主分支,运行 git push,将远程的自己的功能分支与本地同步


D:提交 PR,告知组长审核

目前的进展是这样的:

  • 你已经将远程的你的功能分支和本地功能分支同步了
  • 但是远程的主分支却还没有与你的开发分支同步

这个同步不是你来做的,而是组长来做的,你需要在 GitHub 上(也就是远程仓库上)Pull Request 界面提交一个新的 Pull Request,并说明你的功能分支开发了什么内容,并告知组长审核这个 Pull Request

这个 request 的目的是:将你的远程开发分支合并到远程主分支上,将你做的更改与其他开发人员在之后的开发流程中共享

之所以不能直接合并,而是只能提出请求,是因为你开发的新功能还没有经过测试和检验,可能会对主分支的代码造成混淆或者损坏,所以需要项目负责人来审核

当组长审核完毕,你这次的开发任务就正式完成了